From afda934c1db59d9187e4f3f7a67a221ec2f6591c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 24 Jun 2025 09:55:27 +0800 Subject: [PATCH] reactor:【IoT 物联网】调整数据流转的包结构 --- yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java | 30 ------------------------------ yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataRuleSinkTypeEnum.java | 42 ------------------------------------------ yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataSinkTypeEnum.java | 43 +++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java | 84 ------------------------------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataSinkController.java | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java | 2 +- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/package-info.java | 1 + yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkPageReqVO.java | 29 +++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkRespVO.java | 34 ++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkSaveReqVO.java | 40 ++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java | 29 ----------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java | 37 ------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java | 46 ---------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java | 35 ----------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java | 36 ------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java | 35 ----------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java | 34 ---------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java | 46 ---------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamConfig.java | 34 ---------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java | 39 --------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java | 1 + yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleDO.java | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleSinkDO.java | 69 --------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleSourceDO.java | 79 ------------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataSinkDO.java | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java | 2 +- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeAbstractConfig.java | 35 +++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeHttpConfig.java | 36 ++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeKafkaMQConfig.java | 35 +++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeMqttConfig.java | 34 ++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRabbitMQConfig.java | 46 ++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRedisStreamConfig.java | 34 ++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRocketMQConfig.java | 39 +++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java | 34 ---------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataSinkMapper.java | 32 ++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java | 2 +- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java | 2 +- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java | 64 ---------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java | 77 ----------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java | 86 -------------------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java | 492 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java | 35 ----------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java | 28 ---------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java | 60 ------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java | 55 ------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java | 114 ------------------------------------------------------------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java | 45 --------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java | 88 ---------------------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java | 78 ------------------------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java | 77 ----------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamDataBridgeExecute.java | 80 -------------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java | 65 ----------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkService.java | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkServiceImpl.java | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotRuleSceneDataBridgeAction.java | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/AbstractCacheableDataBridgeExecute.java | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataBridgeExecute.java | 45 +++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataBridgeExecute.java | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotKafkaMQDataBridgeExecute.java | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRabbitMQDataBridgeExecute.java | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisStreamDataBridgeExecute.java | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRocketMQDataBridgeExecute.java | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneService.java | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneServiceImpl.java | 492 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneAction.java | 35 +++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneAlertAction.java | 28 ++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneDeviceControlAction.java | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java | 11 ++++++----- 68 files changed, 2088 insertions(+), 2169 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataRuleSinkTypeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataSinkTypeEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataSinkController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/package-info.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkPageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkSaveReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamConfig.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleSinkDO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleSourceDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataSinkDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeAbstractConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeHttpConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeKafkaMQConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeMqttConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRabbitMQConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRedisStreamConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRocketMQConfig.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataSinkMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamDataBridgeExecute.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotRuleSceneDataBridgeAction.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/AbstractCacheableDataBridgeExecute.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataBridgeExecute.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataBridgeExecute.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotKafkaMQDataBridgeExecute.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRabbitMQDataBridgeExecute.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisStreamDataBridgeExecute.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRocketMQDataBridgeExecute.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneAction.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneAlertAction.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneDeviceControlAction.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java deleted file mode 100644 index a9d445f..0000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.iot.enums.rule; - -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * IoT 数据桥接的方向枚举 - * - * @author 芋道源码 - */ -@RequiredArgsConstructor -@Getter -public enum IotDataBridgeDirectionEnum implements ArrayValuable { - - INPUT(1), // 输入 - OUTPUT(2); // 输出 - - private final Integer type; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgeDirectionEnum::getType).toArray(Integer[]::new); - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataRuleSinkTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataRuleSinkTypeEnum.java deleted file mode 100644 index 824dc78..0000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataRuleSinkTypeEnum.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.iot.enums.rule; - -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * IoT 数据流转的数据目的的类型枚举 - * - * @author 芋道源码 - */ -@RequiredArgsConstructor -@Getter -public enum IotDataRuleSinkTypeEnum implements ArrayValuable { - - HTTP(1, "HTTP"), - TCP(2, "TCP"), - WEBSOCKET(3, "WEBSOCKET"), - - MQTT(10, "MQTT"), - - DATABASE(20, "DATABASE"), - REDIS_STREAM(21, "REDIS_STREAM"), - - ROCKETMQ(30, "ROCKETMQ"), - RABBITMQ(31, "RABBITMQ"), - KAFKA(32, "KAFKA"); - - private final Integer type; - - private final String name; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataRuleSinkTypeEnum::getType).toArray(Integer[]::new); - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataSinkTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataSinkTypeEnum.java new file mode 100644 index 0000000..ed341c6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataSinkTypeEnum.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.iot.enums.rule; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * IoT 数据目的的类型枚举 + * + * @author 芋道源码 + */ +@RequiredArgsConstructor +@Getter +public enum IotDataSinkTypeEnum implements ArrayValuable { + + HTTP(1, "HTTP"), + TCP(2, "TCP"), + WEBSOCKET(3, "WebSocket"), + + MQTT(10, "MQTT"), + + DATABASE(20, "Database"), + // TODO @芋艿:改成 Redis;通过 execute 通用化; + REDIS_STREAM(21, "Redis Stream"), + + ROCKETMQ(30, "RocketMQ"), + RABBITMQ(31, "RabbitMQ"), + KAFKA(32, "Kafka"); + + private final Integer type; + + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataSinkTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java deleted file mode 100644 index 90ff2db..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleSinkDO; -import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - IoT 数据桥梁") -@RestController -@RequestMapping("/iot/data-bridge") -@Validated -public class IotDataBridgeController { - - @Resource - private IotDataBridgeService dataBridgeService; - - @PostMapping("/create") - @Operation(summary = "创建数据桥梁") - @PreAuthorize("@ss.hasPermission('iot:data-bridge:create')") - public CommonResult createDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO createReqVO) { - return success(dataBridgeService.createDataBridge(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新数据桥梁") - @PreAuthorize("@ss.hasPermission('iot:data-bridge:update')") - public CommonResult updateDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO updateReqVO) { - dataBridgeService.updateDataBridge(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除数据桥梁") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:data-bridge:delete')") - public CommonResult deleteDataBridge(@RequestParam("id") Long id) { - dataBridgeService.deleteDataBridge(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得数据桥梁") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')") - public CommonResult getDataBridge(@RequestParam("id") Long id) { - IotDataRuleSinkDO dataBridge = dataBridgeService.getDataBridge(id); - return success(BeanUtils.toBean(dataBridge, IotDataBridgeRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得数据桥梁分页") - @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')") - public CommonResult> getDataBridgePage(@Valid IotDataBridgePageReqVO pageReqVO) { - PageResult pageResult = dataBridgeService.getDataBridgePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotDataBridgeRespVO.class)); - } - - @GetMapping("/simple-list") - @Operation(summary = "获取数据桥梁的精简信息列表", description = "主要用于前端的下拉选项") - public CommonResult> getSimpleDataBridgeList() { - List list = dataBridgeService.getDataBridgeList(CommonStatusEnum.ENABLE.getStatus()); - return success(convertList(list, dataBridge -> // 只返回 id、name 字段 - new IotDataBridgeRespVO().setId(dataBridge.getId()).setName(dataBridge.getName()))); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataSinkController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataSinkController.java new file mode 100644 index 0000000..c5232d3 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataSinkController.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; +import cn.iocoder.yudao.module.iot.service.rule.data.IotDataSinkService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - IoT 数据流转目的") +@RestController +@RequestMapping("/iot/data-sink") +@Validated +public class IotDataSinkController { + + @Resource + private IotDataSinkService dataSinkService; + + @PostMapping("/create") + @Operation(summary = "创建数据目的") + @PreAuthorize("@ss.hasPermission('iot:data-sink:create')") + public CommonResult createDataSink(@Valid @RequestBody IotDataSinkSaveReqVO createReqVO) { + return success(dataSinkService.createDataSink(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新数据目的") + @PreAuthorize("@ss.hasPermission('iot:data-sink:update')") + public CommonResult updateDataSink(@Valid @RequestBody IotDataSinkSaveReqVO updateReqVO) { + dataSinkService.updateDataSink(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除数据目的") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:data-sink:delete')") + public CommonResult deleteDataSink(@RequestParam("id") Long id) { + dataSinkService.deleteDataSink(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得数据目的") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:data-sink:query')") + public CommonResult getDataSink(@RequestParam("id") Long id) { + IotDataSinkDO sink = dataSinkService.getDataSink(id); + return success(BeanUtils.toBean(sink, IotDataSinkRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得数据目的分页") + @PreAuthorize("@ss.hasPermission('iot:data-sink:query')") + public CommonResult> getDataSinkPage(@Valid IotDataSinkPageReqVO pageReqVO) { + PageResult pageResult = dataSinkService.getDataSinkPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotDataSinkRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获取数据目的的精简信息列表", description = "主要用于前端的下拉选项") + public CommonResult> getSimpleDataSinkList() { + List list = dataSinkService.getDataSinkListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, sink -> // 只返回 id、name 字段 + new IotDataSinkRespVO().setId(sink.getId()).setName(sink.getName()))); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java index 5a9cf37..4168daf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePa import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneRespVO; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; -import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; +import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/package-info.java new file mode 100644 index 0000000..6be90cf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data; \ 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/rule/vo/data/sink/IotDataSinkPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkPageReqVO.java new file mode 100644 index 0000000..06bbecc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkPageReqVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - IoT 数据流转目的分页 Request VO") +@Data +public class IotDataSinkPageReqVO extends PageParam { + + @Schema(description = "数据目的名称", example = "赵六") + private String name; + + @Schema(description = "数据目的状态", example = "2") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ 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/rule/vo/data/sink/IotDataSinkRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkRespVO.java new file mode 100644 index 0000000..b00e8a3 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkRespVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataBridgeAbstractConfig; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - IoT 数据流转目的 Response VO") +@Data +public class IotDataSinkRespVO { + + @Schema(description = "数据目的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564") + private Long id; + + @Schema(description = "数据目的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + private String name; + + @Schema(description = "数据目的描述", example = "随便") + private String description; + + @Schema(description = "数据目的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "数据目的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "数据目的配置") + private IotDataBridgeAbstractConfig config; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ 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/rule/vo/data/sink/IotDataSinkSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkSaveReqVO.java new file mode 100644 index 0000000..b178e09 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkSaveReqVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataBridgeAbstractConfig; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 数据流转目的新增/修改 Request VO") +@Data +public class IotDataSinkSaveReqVO { + + @Schema(description = "数据目的编号", example = "18564") + private Long id; + + @Schema(description = "数据目的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @NotEmpty(message = "数据目的名称不能为空") + private String name; + + @Schema(description = "数据目的描述", example = "随便") + private String description; + + @Schema(description = "数据目的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "数据目的状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "数据目的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "数据目的类型不能为空") + @InEnum(IotDataSinkTypeEnum.class) + private Integer type; + + @Schema(description = "数据目的配置") + @NotNull(message = "数据目的配置不能为空") + private IotDataBridgeAbstractConfig config; + +} \ 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/rule/vo/databridge/IotDataBridgePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java deleted file mode 100644 index e4dc36e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IoT 数据桥梁分页 Request VO") -@Data -public class IotDataBridgePageReqVO extends PageParam { - - @Schema(description = "桥梁名称", example = "赵六") - private String name; - - @Schema(description = "桥梁状态", example = "1") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ 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/rule/vo/databridge/IotDataBridgeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java deleted file mode 100644 index 38e04b2..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; - -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT 数据桥梁 Response VO") -@Data -public class IotDataBridgeRespVO { - - @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564") - private Long id; - - @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - private String name; - - @Schema(description = "桥梁描述", example = "随便") - private String description; - - @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED) - private Integer direction; - - @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer type; - - @Schema(description = "桥梁配置") - private IotDataBridgeAbstractConfig config; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ 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/rule/vo/databridge/IotDataBridgeSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java deleted file mode 100644 index ba1bfb1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeDirectionEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataRuleSinkTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - IoT 数据桥梁新增/修改 Request VO") -@Data -public class IotDataBridgeSaveReqVO { - - @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564") - private Long id; - - @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - @NotEmpty(message = "桥梁名称不能为空") - private String name; - - @Schema(description = "桥梁描述", example = "随便") - private String description; - - @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @NotNull(message = "桥梁状态不能为空") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "桥梁方向不能为空") - @InEnum(IotDataBridgeDirectionEnum.class) - private Integer direction; - - @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "桥梁类型不能为空") - @InEnum(IotDataRuleSinkTypeEnum.class) - private Integer type; - - @Schema(description = "桥梁配置") - @NotNull(message = "桥梁配置不能为空") - private IotDataBridgeAbstractConfig config; - -} \ 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/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java deleted file mode 100644 index 62fa555..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; - -import cn.iocoder.yudao.module.iot.enums.rule.IotDataRuleSinkTypeEnum; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import lombok.Data; - -/** - * IoT IotDataBridgeConfig 抽象类 - * - * 用于表示数据桥梁配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类 - * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 - * - * @author HUIHUI - */ -@Data -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) -@JsonSubTypes({ - @JsonSubTypes.Type(value = IotDataBridgeHttpConfig.class, name = "1"), - @JsonSubTypes.Type(value = IotDataBridgeMqttConfig.class, name = "10"), - @JsonSubTypes.Type(value = IotDataBridgeRedisStreamConfig.class, name = "21"), - @JsonSubTypes.Type(value = IotDataBridgeRocketMQConfig.class, name = "30"), - @JsonSubTypes.Type(value = IotDataBridgeRabbitMQConfig.class, name = "31"), - @JsonSubTypes.Type(value = IotDataBridgeKafkaMQConfig.class, name = "32"), -}) -public abstract class IotDataBridgeAbstractConfig { - - /** - * 配置类型 - * - * 枚举 {@link IotDataRuleSinkTypeEnum#getType()} - */ - private String type; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java deleted file mode 100644 index eca35c7..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; - -import lombok.Data; - -import java.util.Map; - -/** - * IoT HTTP 配置 {@link IotDataBridgeAbstractConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataBridgeHttpConfig extends IotDataBridgeAbstractConfig { - - /** - * 请求 URL - */ - private String url; - /** - * 请求方法 - */ - private String method; - /** - * 请求头 - */ - private Map headers; - /** - * 请求参数 - */ - private Map query; - /** - * 请求体 - */ - private String body; - -} \ 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/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java deleted file mode 100644 index 1201214..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; - -import lombok.Data; - -/** - * IoT Kafka 配置 {@link IotDataBridgeAbstractConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataBridgeKafkaMQConfig extends IotDataBridgeAbstractConfig { - - /** - * Kafka 服务器地址 - */ - private String bootstrapServers; - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - /** - * 是否启用 SSL - */ - private Boolean ssl; - - /** - * 主题 - */ - private String topic; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java deleted file mode 100644 index 448b215..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; - -import lombok.Data; - -/** - * IoT MQTT 配置 {@link IotDataBridgeAbstractConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataBridgeMqttConfig extends IotDataBridgeAbstractConfig { - - /** - * MQTT 服务器地址 - */ - private String url; - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - /** - * 客户端编号 - */ - private String clientId; - /** - * 主题 - */ - private String topic; - -} \ 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/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java deleted file mode 100644 index 2c247d1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; - -import lombok.Data; - -/** - * IoT RabbitMQ 配置 {@link IotDataBridgeAbstractConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataBridgeRabbitMQConfig extends IotDataBridgeAbstractConfig { - - /** - * RabbitMQ 服务器地址 - */ - private String host; - /** - * 端口 - */ - private Integer port; - /** - * 虚拟主机 - */ - private String virtualHost; - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - - /** - * 交换机名称 - */ - private String exchange; - /** - * 路由键 - */ - private String routingKey; - /** - * 队列名称 - */ - private String queue; -} \ 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/rule/vo/databridge/config/IotDataBridgeRedisStreamConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamConfig.java deleted file mode 100644 index fc7a4c3..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; - -import lombok.Data; - -/** - * IoT Redis Stream 配置 {@link IotDataBridgeAbstractConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataBridgeRedisStreamConfig extends IotDataBridgeAbstractConfig { - - /** - * Redis 服务器地址 - */ - private String host; - /** - * 端口 - */ - private Integer port; - /** - * 密码 - */ - private String password; - /** - * 数据库索引 - */ - private Integer database; - - /** - * 主题 - */ - private String topic; -} \ 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/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java deleted file mode 100644 index e23e306..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; - -import lombok.Data; - -/** - * IoT RocketMQ 配置 {@link IotDataBridgeAbstractConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataBridgeRocketMQConfig extends IotDataBridgeAbstractConfig { - - /** - * RocketMQ 名称服务器地址 - */ - private String nameServer; - /** - * 访问密钥 - */ - private String accessKey; - /** - * 秘密钥匙 - */ - private String secretKey; - - /** - * 生产者组 - */ - private String group; - /** - * 主题 - */ - private String topic; - /** - * 标签 - */ - private String tags; - -} \ 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/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index 06cc438..bf6e20b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -9,6 +9,7 @@ import lombok.Data; import java.util.List; +// TODO @puhui999:感觉这个,是不是放到 dal 里会好点?(讨论下,先不改哈) /** * IoT 物模型中的事件 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleDO.java index 024e71c..1cf766c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleDO.java @@ -1,8 +1,15 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.rule; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -11,9 +18,9 @@ import lombok.NoArgsConstructor; import java.util.List; /** - * IoT 数据流转 DO + * IoT 数据流转规则 DO * - * 组合 {@link IotDataRuleSourceDO} => {@link IotDataRuleSinkDO} + * 监听 {@link SourceConfig} 数据源,转发到 {@link IotDataSinkDO} 数据目的 * * @author 芋道源码 */ @@ -45,18 +52,54 @@ public class IotDataRuleDO { private Integer status; /** - * 数据源编号 - * - * 关联 {@link IotDataRuleSourceDO#getId()} + * 数据源配置数组 */ - private List sourceIds; + @TableField(typeHandler = JacksonTypeHandler.class) + private List sourceConfigs; /** * 数据目的编号 * - * 关联 {@link IotDataRuleSinkDO#getId()} + * 关联 {@link IotDataSinkDO#getId()} */ + @TableField(typeHandler = LongListTypeHandler.class) private List sinkIds; // TODO @芋艿:未来考虑使用 groovy;支持数据处理; + /** + * 数据源配置 + */ + @Data + public static class SourceConfig { + + /** + * 消息方法 + * + * 枚举 {@link IotDeviceMessageMethodEnum} 中的 upstream 上行部分 + */ + private String method; + + /** + * 产品编号 + * + * 关联 {@link IotProductDO#getId()} + */ + private Long productId; + /** + * 设备编号 + * + * 关联 {@link IotDeviceDO#getId()} + * 特殊:如果为 {@link IotDeviceDO#DEVICE_ID_ALL} 时,则是全部设备 + */ + private Long deviceId; + + /** + * 标识符 + * + * 1. 物模型时,对应:{@link IotThingModelDO#getIdentifier()} + */ + private String identifier; + + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleSinkDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleSinkDO.java deleted file mode 100644 index 1214e71..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleSinkDO.java +++ /dev/null @@ -1,69 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeDirectionEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataRuleSinkTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.*; - -/** - * IoT 数据流转的数据目的 DO - * - * @author 芋道源码 - */ -@TableName(value = "iot_data_bridge", autoResultMap = true) -@KeySequence("iot_data_bridge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDataRuleSinkDO extends BaseDO { - - /** - * 数据目的编号 - */ - @TableId - private Long id; - /** - * 数据目的名称 - */ - private String name; - /** - * 数据目的描述 - */ - private String description; - /** - * 桥梁状态 - * - * 枚举 {@link CommonStatusEnum} - */ - @Deprecated // TODO @puhui999:这个删除 - private Integer status; - /** - * 桥梁方向 - * - * 枚举 {@link IotDataBridgeDirectionEnum} - */ - @Deprecated // TODO @puhui999:这个删除 - private Integer direction; - - /** - * 数据目的类型 - * - * 枚举 {@link IotDataRuleSinkTypeEnum} - */ - private Integer type; - - /** - * 数据目的配置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private IotDataBridgeAbstractConfig config; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleSourceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleSourceDO.java deleted file mode 100644 index 87dd36a..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleSourceDO.java +++ /dev/null @@ -1,79 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule; - -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * IoT 数据流转的数据源 DO - * - * @author 芋道源码 - */ -@TableName(value = "iot_data_flow_source", autoResultMap = true) -@KeySequence("iot_data_flow_source_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDataRuleSourceDO { - - /** - * 数据源编号 - */ - private Long id; - /** - * 数据源名称 - */ - private String name; - - /** - * 配置数组 - */ - private List configs; - - /** - * 配置 - */ - @Data - public static class Config { - - /** - * 消息方法 - * - * 枚举 {@link IotDeviceMessageMethodEnum} 中的 upstream 上行部分 - */ - private String method; - - /** - * 产品编号 - * - * 关联 {@link IotProductDO#getId()} - */ - private Long productId; - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - * 特殊:如果为 {@link IotDeviceDO#DEVICE_ID_ALL} 时,则是全部设备 - */ - private Long deviceId; - - /** - * 标识符 - * - * 1. 物模型时,对应:{@link IotThingModelDO#getIdentifier()} - */ - private String identifier; - - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataSinkDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataSinkDO.java new file mode 100644 index 0000000..16b3d63 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataSinkDO.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataBridgeAbstractConfig; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * IoT 数据流转目的 DO + * + * @author 芋道源码 + */ +@TableName(value = "iot_data_sink", autoResultMap = true) +@KeySequence("iot_data_bridge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotDataSinkDO extends BaseDO { + + /** + * 数据目的编号 + */ + @TableId + private Long id; + /** + * 数据目的名称 + */ + private String name; + /** + * 数据目的描述 + */ + private String description; + /** + * 数据目的状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + + /** + * 数据目的类型 + * + * 枚举 {@link IotDataSinkTypeEnum} + */ + private Integer type; + /** + * 数据目的配置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private IotDataBridgeAbstractConfig config; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java index 741d93d..845b189 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java @@ -201,7 +201,7 @@ public class IotRuleSceneDO extends TenantBaseDO { * 数据桥接编号 * * 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DATA_BRIDGE} 时 - * 关联:{@link IotDataRuleSinkDO#getId()} + * 关联:{@link IotDataSinkDO#getId()} */ private Long dataBridgeId; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeAbstractConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeAbstractConfig.java new file mode 100644 index 0000000..d15008b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeAbstractConfig.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; + +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; + +/** + * IoT IotDataBridgeConfig 抽象类 + * + * 用于表示数据目的配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类 + * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 + * + * @author HUIHUI + */ +@Data +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = IotDataBridgeHttpConfig.class, name = "1"), + @JsonSubTypes.Type(value = IotDataBridgeMqttConfig.class, name = "10"), + @JsonSubTypes.Type(value = IotDataBridgeRedisStreamConfig.class, name = "21"), + @JsonSubTypes.Type(value = IotDataBridgeRocketMQConfig.class, name = "30"), + @JsonSubTypes.Type(value = IotDataBridgeRabbitMQConfig.class, name = "31"), + @JsonSubTypes.Type(value = IotDataBridgeKafkaMQConfig.class, name = "32"), +}) +public abstract class IotDataBridgeAbstractConfig { + + /** + * 配置类型 + * + * 枚举 {@link IotDataSinkTypeEnum#getType()} + */ + private String type; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeHttpConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeHttpConfig.java new file mode 100644 index 0000000..7e65bd4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeHttpConfig.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; + +import lombok.Data; + +import java.util.Map; + +/** + * IoT HTTP 配置 {@link IotDataBridgeAbstractConfig} 实现类 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeHttpConfig extends IotDataBridgeAbstractConfig { + + /** + * 请求 URL + */ + private String url; + /** + * 请求方法 + */ + private String method; + /** + * 请求头 + */ + private Map headers; + /** + * 请求参数 + */ + private Map query; + /** + * 请求体 + */ + private String body; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeKafkaMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeKafkaMQConfig.java new file mode 100644 index 0000000..e0ecd43 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeKafkaMQConfig.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; + +import lombok.Data; + +/** + * IoT Kafka 配置 {@link IotDataBridgeAbstractConfig} 实现类 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeKafkaMQConfig extends IotDataBridgeAbstractConfig { + + /** + * Kafka 服务器地址 + */ + private String bootstrapServers; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 是否启用 SSL + */ + private Boolean ssl; + + /** + * 主题 + */ + private String topic; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeMqttConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeMqttConfig.java new file mode 100644 index 0000000..7500fa3 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeMqttConfig.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; + +import lombok.Data; + +/** + * IoT MQTT 配置 {@link IotDataBridgeAbstractConfig} 实现类 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeMqttConfig extends IotDataBridgeAbstractConfig { + + /** + * MQTT 服务器地址 + */ + private String url; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 客户端编号 + */ + private String clientId; + /** + * 主题 + */ + private String topic; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRabbitMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRabbitMQConfig.java new file mode 100644 index 0000000..b899b5f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRabbitMQConfig.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; + +import lombok.Data; + +/** + * IoT RabbitMQ 配置 {@link IotDataBridgeAbstractConfig} 实现类 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeRabbitMQConfig extends IotDataBridgeAbstractConfig { + + /** + * RabbitMQ 服务器地址 + */ + private String host; + /** + * 端口 + */ + private Integer port; + /** + * 虚拟主机 + */ + private String virtualHost; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + + /** + * 交换机名称 + */ + private String exchange; + /** + * 路由键 + */ + private String routingKey; + /** + * 队列名称 + */ + private String queue; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRedisStreamConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRedisStreamConfig.java new file mode 100644 index 0000000..5ece7f6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRedisStreamConfig.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; + +import lombok.Data; + +/** + * IoT Redis Stream 配置 {@link IotDataBridgeAbstractConfig} 实现类 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeRedisStreamConfig extends IotDataBridgeAbstractConfig { + + /** + * Redis 服务器地址 + */ + private String host; + /** + * 端口 + */ + private Integer port; + /** + * 密码 + */ + private String password; + /** + * 数据库索引 + */ + private Integer database; + + /** + * 主题 + */ + private String topic; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRocketMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRocketMQConfig.java new file mode 100644 index 0000000..4cc73c5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataBridgeRocketMQConfig.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; + +import lombok.Data; + +/** + * IoT RocketMQ 配置 {@link IotDataBridgeAbstractConfig} 实现类 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeRocketMQConfig extends IotDataBridgeAbstractConfig { + + /** + * RocketMQ 名称服务器地址 + */ + private String nameServer; + /** + * 访问密钥 + */ + private String accessKey; + /** + * 秘密钥匙 + */ + private String secretKey; + + /** + * 生产者组 + */ + private String group; + /** + * 主题 + */ + private String topic; + /** + * 标签 + */ + private String tags; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java deleted file mode 100644 index dc7f096..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.rule; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleSinkDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 数据桥梁 Mapper - * - * @author HUIHUI - */ -@Mapper -public interface IotDataBridgeMapper extends BaseMapperX { - - default PageResult selectPage(IotDataBridgePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotDataRuleSinkDO::getName, reqVO.getName()) - .eqIfPresent(IotDataRuleSinkDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(IotDataRuleSinkDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(IotDataRuleSinkDO::getId)); - } - - default List selectList(Integer status) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(IotDataRuleSinkDO::getStatus, status) - .orderByDesc(IotDataRuleSinkDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataSinkMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataSinkMapper.java new file mode 100644 index 0000000..e65001d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataSinkMapper.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.rule; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * IoT 数据流转目的 Mapper + * + * @author HUIHUI + */ +@Mapper +public interface IotDataSinkMapper extends BaseMapperX { + + default PageResult selectPage(IotDataSinkPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(IotDataSinkDO::getName, reqVO.getName()) + .eqIfPresent(IotDataSinkDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(IotDataSinkDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(IotDataSinkDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(IotDataSinkDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java index 594f9ef..352162e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.job.rule; import cn.hutool.core.map.MapUtil; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; +import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.quartz.JobExecutionContext; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java index 38bc342..4212a78 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.mq.consumer.rule; import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; +import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java deleted file mode 100644 index 8559940..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleSinkDO; -import jakarta.validation.Valid; - -import java.util.List; - -/** - * IoT 数据桥梁 Service 接口 - * - * @author HUIHUI - */ -public interface IotDataBridgeService { - - /** - * 创建数据桥梁 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createDataBridge(@Valid IotDataBridgeSaveReqVO createReqVO); - - /** - * 更新数据桥梁 - * - * @param updateReqVO 更新信息 - */ - void updateDataBridge(@Valid IotDataBridgeSaveReqVO updateReqVO); - - /** - * 删除数据桥梁 - * - * @param id 编号 - */ - void deleteDataBridge(Long id); - - /** - * 获得数据桥梁 - * - * @param id 编号 - * @return 数据桥梁 - */ - IotDataRuleSinkDO getDataBridge(Long id); - - /** - * 获得数据桥梁分页 - * - * @param pageReqVO 分页查询 - * @return 数据桥梁分页 - */ - PageResult getDataBridgePage(IotDataBridgePageReqVO pageReqVO); - - /** - * 获取数据桥梁列表 - * - * @param status 状态,如果为空,则不进行筛选 - * @return 数据桥梁列表 - */ - List getDataBridgeList(Integer status); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java deleted file mode 100644 index f10ebc0..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleSinkDO; -import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataBridgeMapper; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_BRIDGE_NOT_EXISTS; - -/** - * IoT 数据桥梁 Service 实现类 - * - * @author HUIHUI - */ -@Service -@Validated -public class IotDataBridgeServiceImpl implements IotDataBridgeService { - - @Resource - private IotDataBridgeMapper dataBridgeMapper; - - @Override - public Long createDataBridge(IotDataBridgeSaveReqVO createReqVO) { - // 插入 - IotDataRuleSinkDO dataBridge = BeanUtils.toBean(createReqVO, IotDataRuleSinkDO.class); - dataBridgeMapper.insert(dataBridge); - // 返回 - return dataBridge.getId(); - } - - @Override - public void updateDataBridge(IotDataBridgeSaveReqVO updateReqVO) { - // 校验存在 - validateDataBridgeExists(updateReqVO.getId()); - // 更新 - IotDataRuleSinkDO updateObj = BeanUtils.toBean(updateReqVO, IotDataRuleSinkDO.class); - dataBridgeMapper.updateById(updateObj); - } - - @Override - public void deleteDataBridge(Long id) { - // 校验存在 - validateDataBridgeExists(id); - // 删除 - dataBridgeMapper.deleteById(id); - } - - private void validateDataBridgeExists(Long id) { - if (dataBridgeMapper.selectById(id) == null) { - throw exception(DATA_BRIDGE_NOT_EXISTS); - } - } - - @Override - public IotDataRuleSinkDO getDataBridge(Long id) { - return dataBridgeMapper.selectById(id); - } - - @Override - public PageResult getDataBridgePage(IotDataBridgePageReqVO pageReqVO) { - return dataBridgeMapper.selectPage(pageReqVO); - } - - @Override - public List getDataBridgeList(Integer status) { - return dataBridgeMapper.selectList(status); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java deleted file mode 100644 index ba137bc..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java +++ /dev/null @@ -1,86 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; -import jakarta.validation.Valid; - -import java.util.List; - -/** - * IoT 规则场景 Service 接口 - * - * @author 芋道源码 - */ -public interface IotRuleSceneService { - - /** - * 创建场景联动 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createRuleScene(@Valid IotRuleSceneSaveReqVO createReqVO); - - /** - * 更新场景联动 - * - * @param updateReqVO 更新信息 - */ - void updateRuleScene(@Valid IotRuleSceneSaveReqVO updateReqVO); - - /** - * 删除场景联动 - * - * @param id 编号 - */ - void deleteRuleScene(Long id); - - /** - * 获得场景联动 - * - * @param id 编号 - * @return 场景联动 - */ - IotRuleSceneDO getRuleScene(Long id); - - /** - * 获得场景联动分页 - * - * @param pageReqVO 分页查询 - * @return 场景联动分页 - */ - PageResult getRuleScenePage(IotRuleScenePageReqVO pageReqVO); - - /** - * 【缓存】获得指定设备的场景列表 - * - * @param productKey 产品 Key - * @param deviceName 设备名称 - * @return 场景列表 - */ - List getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName); - - /** - * 基于 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 场景,执行规则场景 - * - * @param message 消息 - */ - void executeRuleSceneByDevice(IotDeviceMessage message); - - /** - * 基于 {@link IotRuleSceneTriggerTypeEnum#TIMER} 场景,执行规则场景 - * - * @param id 场景编号 - */ - void executeRuleSceneByTimer(Long id); - - /** - * TODO 芋艿:测试方法,需要删除 - */ - void test(); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java deleted file mode 100644 index 2553cdc..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java +++ /dev/null @@ -1,492 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.text.CharPool; -import cn.hutool.core.util.NumberUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.number.NumberUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; -import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionOperatorEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; -import cn.iocoder.yudao.module.iot.job.rule.IotRuleSceneJob; -import cn.iocoder.yudao.module.iot.service.rule.action.IotRuleSceneAction; -import jakarta.annotation.Resource; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.quartz.JobKey; -import org.quartz.Scheduler; -import org.quartz.SchedulerException; -import org.quartz.TriggerKey; -import org.quartz.impl.StdSchedulerFactory; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RULE_SCENE_NOT_EXISTS; - -/** - * IoT 规则场景 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class IotRuleSceneServiceImpl implements IotRuleSceneService { - - @Resource - private IotRuleSceneMapper ruleSceneMapper; - - @Resource - private List ruleSceneActions; - - @Resource(name = "iotSchedulerManager") - private IotSchedulerManager schedulerManager; - - @Override - public Long createRuleScene(IotRuleSceneSaveReqVO createReqVO) { - // 插入 - IotRuleSceneDO ruleScene = BeanUtils.toBean(createReqVO, IotRuleSceneDO.class); - ruleSceneMapper.insert(ruleScene); - // 返回 - return ruleScene.getId(); - } - - @Override - public void updateRuleScene(IotRuleSceneSaveReqVO updateReqVO) { - // 校验存在 - validateRuleSceneExists(updateReqVO.getId()); - // 更新 - IotRuleSceneDO updateObj = BeanUtils.toBean(updateReqVO, IotRuleSceneDO.class); - ruleSceneMapper.updateById(updateObj); - } - - @Override - public void deleteRuleScene(Long id) { - // 校验存在 - validateRuleSceneExists(id); - // 删除 - ruleSceneMapper.deleteById(id); - } - - private void validateRuleSceneExists(Long id) { - if (ruleSceneMapper.selectById(id) == null) { - throw exception(RULE_SCENE_NOT_EXISTS); - } - } - - @Override - public IotRuleSceneDO getRuleScene(Long id) { - return ruleSceneMapper.selectById(id); - } - - @Override - public PageResult getRuleScenePage(IotRuleScenePageReqVO pageReqVO) { - return ruleSceneMapper.selectPage(pageReqVO); - } - - - // TODO 芋艿,缓存待实现 - @Override - @TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略 - public List getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) { - if (true) { - IotRuleSceneDO ruleScene01 = new IotRuleSceneDO(); - ruleScene01.setTriggers(CollUtil.newArrayList()); - IotRuleSceneDO.TriggerConfig trigger01 = new IotRuleSceneDO.TriggerConfig(); - trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType()); - trigger01.setConditions(CollUtil.newArrayList()); - // 属性 - IotRuleSceneDO.TriggerCondition condition01 = new IotRuleSceneDO.TriggerCondition(); - condition01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); - condition01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier()); - condition01.setParameters(CollUtil.newArrayList()); -// IotRuleSceneDO.TriggerConditionParameter parameter010 = new IotRuleSceneDO.TriggerConditionParameter(); -// parameter010.setIdentifier("width"); -// parameter010.setOperator(IotRuleSceneConditionOperatorEnum.EQUALS.getOperator()); -// parameter010.setValue("abc"); -// condition01.getParameters().add(parameter010); - IotRuleSceneDO.TriggerConditionParameter parameter011 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter011.setIdentifier("width"); - parameter011.setOperator(IotRuleSceneConditionOperatorEnum.EQUALS.getOperator()); - parameter011.setValue("1"); - condition01.getParameters().add(parameter011); - IotRuleSceneDO.TriggerConditionParameter parameter012 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter012.setIdentifier("width"); - parameter012.setOperator(IotRuleSceneConditionOperatorEnum.NOT_EQUALS.getOperator()); - parameter012.setValue("2"); - condition01.getParameters().add(parameter012); - IotRuleSceneDO.TriggerConditionParameter parameter013 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter013.setIdentifier("width"); - parameter013.setOperator(IotRuleSceneConditionOperatorEnum.GREATER_THAN.getOperator()); - parameter013.setValue("0"); - condition01.getParameters().add(parameter013); - IotRuleSceneDO.TriggerConditionParameter parameter014 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter014.setIdentifier("width"); - parameter014.setOperator(IotRuleSceneConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator()); - parameter014.setValue("0"); - condition01.getParameters().add(parameter014); - IotRuleSceneDO.TriggerConditionParameter parameter015 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter015.setIdentifier("width"); - parameter015.setOperator(IotRuleSceneConditionOperatorEnum.LESS_THAN.getOperator()); - parameter015.setValue("2"); - condition01.getParameters().add(parameter015); - IotRuleSceneDO.TriggerConditionParameter parameter016 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter016.setIdentifier("width"); - parameter016.setOperator(IotRuleSceneConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator()); - parameter016.setValue("2"); - condition01.getParameters().add(parameter016); - IotRuleSceneDO.TriggerConditionParameter parameter017 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter017.setIdentifier("width"); - parameter017.setOperator(IotRuleSceneConditionOperatorEnum.IN.getOperator()); - parameter017.setValue("1,2,3"); - condition01.getParameters().add(parameter017); - IotRuleSceneDO.TriggerConditionParameter parameter018 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter018.setIdentifier("width"); - parameter018.setOperator(IotRuleSceneConditionOperatorEnum.NOT_IN.getOperator()); - parameter018.setValue("0,2,3"); - condition01.getParameters().add(parameter018); - IotRuleSceneDO.TriggerConditionParameter parameter019 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter019.setIdentifier("width"); - parameter019.setOperator(IotRuleSceneConditionOperatorEnum.BETWEEN.getOperator()); - parameter019.setValue("1,3"); - condition01.getParameters().add(parameter019); - IotRuleSceneDO.TriggerConditionParameter parameter020 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter020.setIdentifier("width"); - parameter020.setOperator(IotRuleSceneConditionOperatorEnum.NOT_BETWEEN.getOperator()); - parameter020.setValue("2,3"); - condition01.getParameters().add(parameter020); - trigger01.getConditions().add(condition01); - // 状态 - IotRuleSceneDO.TriggerCondition condition02 = new IotRuleSceneDO.TriggerCondition(); - condition02.setType(IotDeviceMessageTypeEnum.STATE.getType()); - condition02.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()); - condition02.setParameters(CollUtil.newArrayList()); - trigger01.getConditions().add(condition02); - // 事件 - IotRuleSceneDO.TriggerCondition condition03 = new IotRuleSceneDO.TriggerCondition(); - condition03.setType(IotDeviceMessageTypeEnum.EVENT.getType()); - condition03.setIdentifier("xxx"); - condition03.setParameters(CollUtil.newArrayList()); - IotRuleSceneDO.TriggerConditionParameter parameter030 = new IotRuleSceneDO.TriggerConditionParameter(); - parameter030.setIdentifier("width"); - parameter030.setOperator(IotRuleSceneConditionOperatorEnum.EQUALS.getOperator()); - parameter030.setValue("1"); - trigger01.getConditions().add(condition03); - ruleScene01.getTriggers().add(trigger01); - // 动作 - ruleScene01.setActions(CollUtil.newArrayList()); - // 设备控制 - IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig(); - action01.setType(IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET.getType()); - IotRuleSceneDO.ActionDeviceControl actionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl(); - actionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT"); - actionDeviceControl01.setDeviceNames(ListUtil.of("small")); - actionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); - actionDeviceControl01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()); - actionDeviceControl01.setData(MapUtil.builder() - .put("power", 1) - .put("color", "red") - .build()); - action01.setDeviceControl(actionDeviceControl01); -// ruleScene01.getActions().add(action01); // TODO 芋艿:先不测试了 - // 数据桥接(http) - IotRuleSceneDO.ActionConfig action02 = new IotRuleSceneDO.ActionConfig(); - action02.setType(IotRuleSceneActionTypeEnum.DATA_BRIDGE.getType()); - action02.setDataBridgeId(1L); - ruleScene01.getActions().add(action02); - return ListUtil.toList(ruleScene01); - } - - List list = ruleSceneMapper.selectList(); - // TODO @芋艿:需要考虑开启状态 - return filterList(list, ruleScene -> { - for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) { - if (ObjUtil.notEqual(trigger.getProductKey(), productKey)) { - continue; - } - if (CollUtil.isEmpty(trigger.getDeviceNames()) // 无设备名称限制 - || trigger.getDeviceNames().contains(deviceName)) { // 包含设备名称 - return true; - } - } - return false; - }); - } - - @Override - public void executeRuleSceneByDevice(IotDeviceMessage message) { - // TODO @芋艿:这里的 tenantId,通过设备获取; -// TenantUtils.execute(message.getTenantId(), () -> { -// // 1. 获得设备匹配的规则场景 -// List ruleScenes = getMatchedRuleSceneListByMessage(message); -// if (CollUtil.isEmpty(ruleScenes)) { -// return; -// } -// -// // 2. 执行规则场景 -// executeRuleSceneAction(message, ruleScenes); -// }); - } - - @Override - public void executeRuleSceneByTimer(Long id) { - // 1.1 获得规则场景 -// IotRuleSceneDO scene = TenantUtils.executeIgnore(() -> ruleSceneMapper.selectById(id)); - // TODO @芋艿:这里,临时测试,后续删除。 - IotRuleSceneDO scene = new IotRuleSceneDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); - if (true) { - scene.setTenantId(1L); - IotRuleSceneDO.TriggerConfig triggerConfig = new IotRuleSceneDO.TriggerConfig(); - triggerConfig.setType(IotRuleSceneTriggerTypeEnum.TIMER.getType()); - scene.setTriggers(ListUtil.toList(triggerConfig)); - // 动作 - IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig(); - action01.setType(IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET.getType()); - IotRuleSceneDO.ActionDeviceControl iotRuleSceneActionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl(); - iotRuleSceneActionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT"); - iotRuleSceneActionDeviceControl01.setDeviceNames(ListUtil.of("small")); - iotRuleSceneActionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); - iotRuleSceneActionDeviceControl01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()); - iotRuleSceneActionDeviceControl01.setData(MapUtil.builder() - .put("power", 1) - .put("color", "red") - .build()); - action01.setDeviceControl(iotRuleSceneActionDeviceControl01); - scene.setActions(ListUtil.toList(action01)); - } - if (scene == null) { - log.error("[executeRuleSceneByTimer][规则场景({}) 不存在]", id); - return; - } - if (CommonStatusEnum.isDisable(scene.getStatus())) { - log.info("[executeRuleSceneByTimer][规则场景({}) 已被禁用]", id); - return; - } - // 1.2 判断是否有定时触发器,避免脏数据 - IotRuleSceneDO.TriggerConfig config = CollUtil.findOne(scene.getTriggers(), - trigger -> ObjUtil.equals(trigger.getType(), IotRuleSceneTriggerTypeEnum.TIMER.getType())); - if (config == null) { - log.error("[executeRuleSceneByTimer][规则场景({}) 不存在定时触发器]", scene); - return; - } - - // 2. 执行规则场景 - TenantUtils.execute(scene.getTenantId(), - () -> executeRuleSceneAction(null, ListUtil.toList(scene))); - } - - /** - * 基于消息,获得匹配的规则场景列表 - * - * @param message 设备消息 - * @return 规则场景列表 - */ - private List getMatchedRuleSceneListByMessage(IotDeviceMessage message) { - // 1. 匹配设备 - // TODO @芋艿:可能需要 getSelf(); 缓存 - List ruleScenes = null; - // TODO @芋艿:这里需要适配 -// List ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache( -// message.getProductKey(), message.getDeviceName()); - if (CollUtil.isEmpty(ruleScenes)) { - return ruleScenes; - } - - // 2. 匹配 trigger 触发器的条件 - return filterList(ruleScenes, ruleScene -> { - for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) { - // 2.1 非设备触发,不匹配 - if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) { - return false; - } - // TODO 芋艿:产品、设备的匹配,要不要这里在做一次???貌似和 1. 部分重复了 - // 2.2 条件为空,说明没有匹配的条件,因此不匹配 - if (CollUtil.isEmpty(trigger.getConditions())) { - return false; - } - // 2.3 多个条件,只需要满足一个即可 - IotRuleSceneDO.TriggerCondition matchedCondition = CollUtil.findOne(trigger.getConditions(), condition -> { - // TODO @芋艿:这里的逻辑,需要适配 -// if (ObjUtil.notEqual(message.getType(), condition.getType()) -// || ObjUtil.notEqual(message.getIdentifier(), condition.getIdentifier())) { -// return false; -// } - // 多个条件参数,必须全部满足。所以,下面的逻辑就是找到一个不满足的条件参数 - IotRuleSceneDO.TriggerConditionParameter notMatchedParameter = CollUtil.findOne(condition.getParameters(), - parameter -> !isTriggerConditionParameterMatched(message, parameter, ruleScene, trigger)); - return notMatchedParameter == null; - }); - if (matchedCondition == null) { - return false; - } - log.info("[getMatchedRuleSceneList][消息({}) 匹配到规则场景编号({}) 的触发器({})]", message, ruleScene.getId(), trigger); - return true; - } - return false; - }); - } - - // TODO @芋艿:【可优化】可以考虑增加下单测,边界太多了。 - - /** - * 判断触发器的条件参数是否匹配 - * - * @param message 设备消息 - * @param parameter 触发器条件参数 - * @param ruleScene 规则场景(用于日志,无其它作用) - * @param trigger 触发器(用于日志,无其它作用) - * @return 是否匹配 - */ - @SuppressWarnings({"unchecked", "DataFlowIssue"}) - private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter, - IotRuleSceneDO ruleScene, IotRuleSceneDO.TriggerConfig trigger) { - // 1.1 校验操作符是否合法 - IotRuleSceneConditionOperatorEnum operator = - IotRuleSceneConditionOperatorEnum.operatorOf(parameter.getOperator()); - if (operator == null) { - log.error("[isTriggerConditionParameterMatched][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]", - ruleScene.getId(), trigger, parameter.getOperator()); - return false; - } - // 1.2 校验消息是否包含对应的值 - String messageValue = MapUtil.getStr((Map) message.getData(), parameter.getIdentifier()); - if (messageValue == null) { - return false; - } - - // 2.1 构建 Spring 表达式的变量 - Map springExpressionVariables = new HashMap<>(); - try { - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue); - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE, parameter.getValue()); - List parameterValues = StrUtil.splitTrim(parameter.getValue(), CharPool.COMMA); - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues); - // 特殊:解决数字的比较。因为 Spring 是基于它的 compareTo 方法,对数字的比较存在问题! - if (ObjectUtils.equalsAny(operator, IotRuleSceneConditionOperatorEnum.BETWEEN, - IotRuleSceneConditionOperatorEnum.NOT_BETWEEN, - IotRuleSceneConditionOperatorEnum.GREATER_THAN, - IotRuleSceneConditionOperatorEnum.GREATER_THAN_OR_EQUALS, - IotRuleSceneConditionOperatorEnum.LESS_THAN, - IotRuleSceneConditionOperatorEnum.LESS_THAN_OR_EQUALS) - && NumberUtil.isNumber(messageValue) - && NumberUtils.isAllNumber(parameterValues)) { - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, - NumberUtil.parseDouble(messageValue)); - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE, - NumberUtil.parseDouble(parameter.getValue())); - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, - convertList(parameterValues, NumberUtil::parseDouble)); - } - // 2.2 计算 Spring 表达式 - return (Boolean) SpringExpressionUtils.parseExpression(operator.getSpringExpression(), springExpressionVariables); - } catch (Exception e) { - log.error("[isTriggerConditionParameterMatched][消息({}) 规则场景编号({}) 的触发器({}) 的匹配表达式({}/{}) 计算异常]", - message, ruleScene.getId(), trigger, operator, springExpressionVariables, e); - return false; - } - } - - /** - * 执行规则场景的动作 - * - * @param message 设备消息 - * @param ruleScenes 规则场景列表 - */ - private void executeRuleSceneAction(IotDeviceMessage message, List ruleScenes) { - // 1. 遍历规则场景 - ruleScenes.forEach(ruleScene -> { - // 2. 遍历规则场景的动作 - ruleScene.getActions().forEach(actionConfig -> { - // 3.1 获取对应的动作 Action 数组 - List actions = filterList(ruleSceneActions, - action -> action.getType().getType().equals(actionConfig.getType())); - if (CollUtil.isEmpty(actions)) { - return; - } - // 3.2 执行动作 - actions.forEach(action -> { - try { - action.execute(message, actionConfig); - log.info("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]", - message, ruleScene.getId(), actionConfig); - } catch (Exception e) { - log.error("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 执行异常]", - message, ruleScene.getId(), actionConfig, e); - } - }); - }); - }); - } - - @Override - @SneakyThrows - public void test() { - // TODO @芋艿:测试思路代码,记得删除!!! - // 1. Job 类:IotRuleSceneJob DONE - // 2. 参数:id DONE - // 3. jobHandlerName:IotRuleSceneJob + id DONE - - // 新增:addJob - // 修改:不存在 addJob、存在 updateJob - // 有 + 禁用:1)存在、停止;2)不存在:不处理;TODO 测试:直接暂停,是否可以???(结论:可以)pauseJob - // 有 + 开启:1)存在,更新;2)不存在,新增;结论:使用 save(addOrUpdateJob) - // 无 + 禁用、开启:1)存在,删除;TODO 测试:直接删除???(结论:可以)deleteJob - - // - if (false) { - Long id = 1L; - Map jobDataMap = IotRuleSceneJob.buildJobDataMap(id); - schedulerManager.addOrUpdateJob(IotRuleSceneJob.class, - IotRuleSceneJob.buildJobName(id), - "0/10 * * * * ?", - jobDataMap); - } - if (false) { - Long id = 1L; - schedulerManager.pauseJob(IotRuleSceneJob.buildJobName(id)); - } - if (true) { - Long id = 1L; - schedulerManager.deleteJob(IotRuleSceneJob.buildJobName(id)); - } - } - - public static void main2(String[] args) throws SchedulerException { -// System.out.println(QuartzJobBean.class); - Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); - scheduler.start(); - - String jobHandlerName = "123"; - // 暂停 Trigger 对象 - scheduler.pauseTrigger(new TriggerKey(jobHandlerName)); - // 取消并删除 Job 调度 - scheduler.unscheduleJob(new TriggerKey(jobHandlerName)); - scheduler.deleteJob(new JobKey(jobHandlerName)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java deleted file mode 100644 index d441e5b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; - -import javax.annotation.Nullable; - -/** - * IoT 规则场景的场景执行器接口 - * - * @author 芋道源码 - */ -public interface IotRuleSceneAction { - - // TODO @芋艿:groovy 或者 javascript 实现数据的转换;可以考虑基于 hutool 的 ScriptUtil 做 - - /** - * 执行场景 - * - * @param message 消息,允许空 - * 1. 空的情况:定时触发 - * 2. 非空的情况:设备触发 - * @param config 配置 - */ - void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception; - - /** - * 获得类型 - * - * @return 类型 - */ - IotRuleSceneActionTypeEnum getType(); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java deleted file mode 100644 index 57530f9..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; -import org.springframework.stereotype.Component; - -import javax.annotation.Nullable; - -/** - * IoT 告警的 {@link IotRuleSceneAction} 实现类 - * - * @author 芋道源码 - */ -@Component -public class IotRuleSceneAlertAction implements IotRuleSceneAction { - - @Override - public void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { - // TODO @芋艿:待实现 - } - - @Override - public IotRuleSceneActionTypeEnum getType() { - return IotRuleSceneActionTypeEnum.ALERT; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java deleted file mode 100644 index ca73c8b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action; - -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleSinkDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService; -import cn.iocoder.yudao.module.iot.service.rule.action.databridge.IotDataBridgeExecute; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.List; - -/** - * IoT 数据桥梁的 {@link IotRuleSceneAction} 实现类 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { - - @Resource - private IotDataBridgeService dataBridgeService; - @Resource - private List> dataBridgeExecutes; - - @Override - public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception { - // 1.1 如果消息为空,直接返回 - if (message == null) { - return; - } - // 1.2 获得数据桥梁 - Assert.notNull(config.getDataBridgeId(), "数据桥梁编号不能为空"); - IotDataRuleSinkDO dataBridge = dataBridgeService.getDataBridge(config.getDataBridgeId()); - if (dataBridge == null || dataBridge.getConfig() == null) { - log.error("[execute][message({}) config({}) 对应的数据桥梁不存在]", message, config); - return; - } - if (CommonStatusEnum.isDisable(dataBridge.getStatus())) { - log.info("[execute][message({}) config({}) 对应的数据桥梁({}) 状态为禁用]", message, config, dataBridge); - return; - } - - // 2. 执行数据桥接操作 - for (IotDataBridgeExecute execute : dataBridgeExecutes) { - execute.execute(message, dataBridge); - } - } - - @Override - public IotRuleSceneActionTypeEnum getType() { - return IotRuleSceneActionTypeEnum.DATA_BRIDGE; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java deleted file mode 100644 index a967c3d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action; - -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * IoT 设备控制的 {@link IotRuleSceneAction} 实现类 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotDeviceMessageService deviceMessageService; - - @Override - public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { - IotRuleSceneDO.ActionDeviceControl control = config.getDeviceControl(); - Assert.notNull(control, "设备控制配置不能为空"); - // 遍历每个设备,下发消息 - control.getDeviceNames().forEach(deviceName -> { - IotDeviceDO device = deviceService.getDeviceFromCache(control.getProductKey(), deviceName); - if (device == null) { - log.error("[execute][message({}) config({}) 对应的设备不存在]", message, config); - return; - } - try { - // TODO @芋艿:@puhui999:这块可能要改,从 type => method - IotDeviceMessage downstreamMessage = deviceMessageService.sendDeviceMessage(IotDeviceMessage.requestOf( - control.getType() + control.getIdentifier(), control.getData()).setDeviceId(device.getId())); - log.info("[execute][message({}) config({}) 下发消息({})成功]", message, config, downstreamMessage); - } catch (Exception e) { - log.error("[execute][message({}) config({}) 下发消息失败]", message, config, e); - } - }); - } - - @Override - public IotRuleSceneActionTypeEnum getType() { - return IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java deleted file mode 100644 index 3f5b1e5..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java +++ /dev/null @@ -1,114 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; - -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleSinkDO; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.cache.RemovalListener; -import lombok.extern.slf4j.Slf4j; - -import java.time.Duration; - -// TODO @芋艿:数据库 -// TODO @芋艿:mqtt -// TODO @芋艿:tcp -// TODO @芋艿:websocket - -/** - * 带缓存功能的数据桥梁执行器抽象类 - * - * 该类提供了一个通用的缓存机制,用于管理各类数据桥接的生产者(Producer)实例。 - * - * 主要特点: - * - 基于Guava Cache实现高效的生产者实例缓存管理 - * - 自动处理生产者的生命周期(创建、获取、关闭) - * - 支持30分钟未访问自动过期清理机制 - * - 异常处理与日志记录,便于问题排查 - * - * 子类需要实现: - * - initProducer(Config) - 初始化特定类型的生产者实例 - * - closeProducer(Producer) - 关闭生产者实例并释放资源 - * - * @param 配置信息类型,用于初始化生产者 - * @param 生产者类型,负责将数据发送到目标系统 - * @author HUIHUI - */ -@Slf4j -public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridgeExecute { - - /** - * Producer 缓存 - */ - private final LoadingCache PRODUCER_CACHE = CacheBuilder.newBuilder() - .expireAfterAccess(Duration.ofMinutes(30)) // 30 分钟未访问就提前过期 - .removalListener((RemovalListener) notification -> { - Producer producer = notification.getValue(); - if (producer == null) { - return; - } - - try { - closeProducer(producer); - log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已关闭]", notification.getKey()); - } catch (Exception e) { - log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 关闭失败]", notification.getKey(), e); - } - }) - .build(new CacheLoader() { - - @Override - public Producer load(Config config) throws Exception { - try { - Producer producer = initProducer(config); - log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config); - return producer; - } catch (Exception e) { - log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 创建启动失败]", config, e); - throw e; // 抛出异常,触发缓存加载失败机制 - } - } - - }); - - /** - * 获取生产者 - * - * @param config 配置信息 - * @return 生产者对象 - */ - protected Producer getProducer(Config config) throws Exception { - return PRODUCER_CACHE.get(config); - } - - /** - * 初始化生产者 - * - * @param config 配置信息 - * @return 生产者对象 - * @throws Exception 如果初始化失败 - */ - protected abstract Producer initProducer(Config config) throws Exception; - - /** - * 关闭生产者 - * - * @param producer 生产者对象 - */ - protected abstract void closeProducer(Producer producer) throws Exception; - - @Override - @SuppressWarnings({"unchecked"}) - public void execute(IotDeviceMessage message, IotDataRuleSinkDO dataBridge) { - if (ObjUtil.notEqual(dataBridge.getType(), getType())) { - return; - } - try { - execute0(message, (Config) dataBridge.getConfig()); - } catch (Exception e) { - log.error("[execute][桥梁配置 config({}) 对应的 message({}) 发送异常]", dataBridge.getConfig(), message, e); - } - } - -} \ 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/rule/action/databridge/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java deleted file mode 100644 index d5dab11..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleSinkDO; - -/** - * IoT 数据桥梁的执行器 execute 接口 - * - * @author HUIHUI - */ -public interface IotDataBridgeExecute { - - /** - * 获取数据桥梁类型 - * - * @return 数据桥梁类型 - */ - Integer getType(); - - /** - * 执行数据桥梁操作 - * - * @param message 设备消息 - * @param dataBridge 数据桥梁 - */ - @SuppressWarnings({"unchecked"}) - default void execute(IotDeviceMessage message, IotDataRuleSinkDO dataBridge) throws Exception { - // 1.1 校验数据桥梁类型 - if (!getType().equals(dataBridge.getType())) { - return; - } - - // 1.2 执行对应的数据桥梁发送消息 - execute0(message, (Config) dataBridge.getConfig()); - } - - /** - * 【真正】执行数据桥梁操作 - * - * @param message 设备消息 - * @param config 桥梁配置 - */ - void execute0(IotDeviceMessage message, Config config) throws Exception; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java deleted file mode 100644 index 9d628ac..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java +++ /dev/null @@ -1,88 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeHttpConfig; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataRuleSinkTypeEnum; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.*; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import java.util.HashMap; -import java.util.Map; - -/** - * Http 的 {@link IotDataBridgeExecute} 实现类 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotHttpDataBridgeExecute implements IotDataBridgeExecute { - - @Resource - private RestTemplate restTemplate; - - @Override - public Integer getType() { - return IotDataRuleSinkTypeEnum.HTTP.getType(); - } - - @Override - @SuppressWarnings({"unchecked", "deprecation"}) - public void execute0(IotDeviceMessage message, IotDataBridgeHttpConfig config) { - String url = null; - HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase()); - HttpEntity requestEntity = null; - ResponseEntity responseEntity = null; - try { - // 1.1 构建 Header - HttpHeaders headers = new HttpHeaders(); - if (CollUtil.isNotEmpty(config.getHeaders())) { - config.getHeaders().putAll(config.getHeaders()); - } - // TODO @puhui999:@yunai:可能需要通过设备查询到租户,然后 set -// headers.add(HEADER_TENANT_ID, message.getTenantId().toString()); - // 1.2 构建 URL - UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(config.getUrl()); - if (CollUtil.isNotEmpty(config.getQuery())) { - config.getQuery().forEach(uriBuilder::queryParam); - } - // 1.3 构建请求体 - if (method == HttpMethod.GET) { - uriBuilder.queryParam("message", HttpUtils.encodeUtf8(JsonUtils.toJsonString(message))); - url = uriBuilder.build().toUriString(); - requestEntity = new HttpEntity<>(headers); - } else { - url = uriBuilder.build().toUriString(); - Map requestBody = JsonUtils.parseObject(config.getBody(), Map.class); - if (requestBody == null) { - requestBody = new HashMap<>(); - } - requestBody.put("message", message); - headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); - requestEntity = new HttpEntity<>(JsonUtils.toJsonString(requestBody), headers); - } - - // 2.1 发送请求 - responseEntity = restTemplate.exchange(url, method, requestEntity, String.class); - // 2.2 记录日志 - if (responseEntity.getStatusCode().is2xxSuccessful()) { - log.info("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求成功({})]", - message, config, url, method, requestEntity, responseEntity); - } else { - log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求失败({})]", - message, config, url, method, requestEntity, responseEntity); - } - } catch (Exception e) { - log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求异常({})]", - message, config, url, method, requestEntity, responseEntity, e); - } - } - -} \ 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/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java deleted file mode 100644 index 3823e2d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ /dev/null @@ -1,78 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; - -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeKafkaMQConfig; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataRuleSinkTypeEnum; -import lombok.extern.slf4j.Slf4j; -import org.apache.kafka.clients.producer.ProducerConfig; -import org.apache.kafka.common.serialization.StringSerializer; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.kafka.core.DefaultKafkaProducerFactory; -import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.stereotype.Component; - -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * Kafka 的 {@link IotDataBridgeExecute} 实现类 - * - * @author HUIHUI - */ -@ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate") -@Component -@Slf4j -public class IotKafkaMQDataBridgeExecute extends - AbstractCacheableDataBridgeExecute> { - - private static final Duration SEND_TIMEOUT = Duration.ofMillis(10000); // 10 秒超时时间 - - @Override - public Integer getType() { - return IotDataRuleSinkTypeEnum.KAFKA.getType(); - } - - @Override - public void execute0(IotDeviceMessage message, IotDataBridgeKafkaMQConfig config) throws Exception { - // 1. 获取或创建 KafkaTemplate - KafkaTemplate kafkaTemplate = getProducer(config); - - // 2. 发送消息并等待结果 - kafkaTemplate.send(config.getTopic(), message.toString()) - .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS); // 添加超时等待 - log.info("[execute0][message({}) 发送成功]", message); - } - - @Override - protected KafkaTemplate initProducer(IotDataBridgeKafkaMQConfig config) { - // 1.1 构建生产者配置 - Map props = new HashMap<>(); - props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers()); - props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - // 1.2 如果配置了认证信息 - if (config.getUsername() != null && config.getPassword() != null) { - props.put("security.protocol", "SASL_PLAINTEXT"); - props.put("sasl.mechanism", "PLAIN"); - props.put("sasl.jaas.config", - "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"" - + config.getUsername() + "\" password=\"" + config.getPassword() + "\";"); - } - // 1.3 如果启用 SSL - if (Boolean.TRUE.equals(config.getSsl())) { - props.put("security.protocol", "SSL"); - } - - // 2. 创建 KafkaTemplate - DefaultKafkaProducerFactory producerFactory = new DefaultKafkaProducerFactory<>(props); - return new KafkaTemplate<>(producerFactory); - } - - @Override - protected void closeProducer(KafkaTemplate producer) { - producer.destroy(); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java deleted file mode 100644 index 0cce764..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; - -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRabbitMQConfig; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataRuleSinkTypeEnum; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.stereotype.Component; - -import java.nio.charset.StandardCharsets; - -/** - * RabbitMQ 的 {@link IotDataBridgeExecute} 实现类 - * - * @author HUIHUI - */ -@ConditionalOnClass(name = "com.rabbitmq.client.Channel") -@Component -@Slf4j -public class IotRabbitMQDataBridgeExecute extends - AbstractCacheableDataBridgeExecute { - - - @Override - public Integer getType() { - return IotDataRuleSinkTypeEnum.RABBITMQ.getType(); - } - - @Override - public void execute0(IotDeviceMessage message, IotDataBridgeRabbitMQConfig config) throws Exception { - // 1. 获取或创建 Channel - Channel channel = getProducer(config); - - // 2.1 声明交换机、队列和绑定关系 - channel.exchangeDeclare(config.getExchange(), "direct", true); - channel.queueDeclare(config.getQueue(), true, false, false, null); - channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey()); - - // 2.2 发送消息 - channel.basicPublish(config.getExchange(), config.getRoutingKey(), null, - message.toString().getBytes(StandardCharsets.UTF_8)); - log.info("[executeRabbitMQ][message({}) config({}) 发送成功]", message, config); - } - - @Override - @SuppressWarnings("resource") - protected Channel initProducer(IotDataBridgeRabbitMQConfig config) throws Exception { - // 1. 创建连接工厂 - ConnectionFactory factory = new ConnectionFactory(); - factory.setHost(config.getHost()); - factory.setPort(config.getPort()); - factory.setVirtualHost(config.getVirtualHost()); - factory.setUsername(config.getUsername()); - factory.setPassword(config.getPassword()); - - // 2. 创建连接 - Connection connection = factory.newConnection(); - - // 3. 创建信道 - return connection.createChannel(); - } - - @Override - protected void closeProducer(Channel channel) throws Exception { - if (channel.isOpen()) { - channel.close(); - } - Connection connection = channel.getConnection(); - if (connection.isOpen()) { - connection.close(); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamDataBridgeExecute.java deleted file mode 100644 index e7ab22c..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamDataBridgeExecute.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRedisStreamConfig; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataRuleSinkTypeEnum; -import lombok.extern.slf4j.Slf4j; -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.config.Config; -import org.redisson.config.SingleServerConfig; -import org.redisson.spring.data.connection.RedissonConnectionFactory; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.stream.ObjectRecord; -import org.springframework.data.redis.connection.stream.StreamRecords; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.stereotype.Component; - -/** - * Redis Stream 的 {@link IotDataBridgeExecute} 实现类 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotRedisStreamDataBridgeExecute extends - AbstractCacheableDataBridgeExecute> { - - @Override - public Integer getType() { - return IotDataRuleSinkTypeEnum.REDIS_STREAM.getType(); - } - - @Override - public void execute0(IotDeviceMessage message, IotDataBridgeRedisStreamConfig config) throws Exception { - // 1. 获取 RedisTemplate - RedisTemplate redisTemplate = getProducer(config); - - // 2. 创建并发送 Stream 记录 - ObjectRecord record = StreamRecords.newRecord() - .ofObject(message).withStreamKey(config.getTopic()); - String recordId = String.valueOf(redisTemplate.opsForStream().add(record)); - log.info("[executeRedisStream][消息发送成功] messageId: {}, config: {}", recordId, config); - } - - @Override - protected RedisTemplate initProducer(IotDataBridgeRedisStreamConfig config) { - // 1.1 创建 Redisson 配置 - Config redissonConfig = new Config(); - SingleServerConfig serverConfig = redissonConfig.useSingleServer() - .setAddress("redis://" + config.getHost() + ":" + config.getPort()) - .setDatabase(config.getDatabase()); - // 1.2 设置密码(如果有) - if (StrUtil.isNotBlank(config.getPassword())) { - serverConfig.setPassword(config.getPassword()); - } - - // 创建 RedisTemplate 并配置 - RedissonClient redisson = Redisson.create(redissonConfig); - RedisTemplate template = new RedisTemplate<>(); - template.setConnectionFactory(new RedissonConnectionFactory(redisson)); - // 设置序列化器 - template.setKeySerializer(RedisSerializer.string()); - template.setHashKeySerializer(RedisSerializer.string()); - template.setValueSerializer(RedisSerializer.json()); - template.setHashValueSerializer(RedisSerializer.json()); - template.afterPropertiesSet(); - return template; - } - - @Override - protected void closeProducer(RedisTemplate producer) throws Exception { - RedisConnectionFactory factory = producer.getConnectionFactory(); - if (factory != null) { - ((RedissonConnectionFactory) factory).destroy(); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java deleted file mode 100644 index 1464f6a..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.action.databridge; - -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRocketMQConfig; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataRuleSinkTypeEnum; -import lombok.extern.slf4j.Slf4j; -import org.apache.rocketmq.client.producer.DefaultMQProducer; -import org.apache.rocketmq.client.producer.SendResult; -import org.apache.rocketmq.client.producer.SendStatus; -import org.apache.rocketmq.common.message.Message; -import org.apache.rocketmq.remoting.common.RemotingHelper; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.stereotype.Component; - -/** - * RocketMQ 的 {@link IotDataBridgeExecute} 实现类 - * - * @author HUIHUI - */ -@ConditionalOnClass(name = "org.apache.rocketmq.client.producer.DefaultMQProducer") -@Component -@Slf4j -public class IotRocketMQDataBridgeExecute extends - AbstractCacheableDataBridgeExecute { - - @Override - public Integer getType() { - return IotDataRuleSinkTypeEnum.ROCKETMQ.getType(); - } - - @Override - public void execute0(IotDeviceMessage message, IotDataBridgeRocketMQConfig config) throws Exception { - // 1. 获取或创建 Producer - DefaultMQProducer producer = getProducer(config); - - // 2.1 创建消息对象,指定Topic、Tag和消息体 - Message msg = new Message( - config.getTopic(), - config.getTags(), - message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET) - ); - // 2.2 发送同步消息并处理结果 - SendResult sendResult = producer.send(msg); - // 2.3 处理发送结果 - if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { - log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", message, config, sendResult); - } else { - log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", message, config, sendResult); - } - } - - @Override - protected DefaultMQProducer initProducer(IotDataBridgeRocketMQConfig config) throws Exception { - DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); - producer.setNamesrvAddr(config.getNameServer()); - producer.start(); - return producer; - } - - @Override - protected void closeProducer(DefaultMQProducer producer) { - producer.shutdown(); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkService.java new file mode 100644 index 0000000..6057303 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkService.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.iot.service.rule.data; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * IoT 数据流转目的 Service 接口 + * + * @author HUIHUI + */ +public interface IotDataSinkService { + + /** + * 创建数据目的 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDataSink(@Valid IotDataSinkSaveReqVO createReqVO); + + /** + * 更新数据目的 + * + * @param updateReqVO 更新信息 + */ + void updateDataSink(@Valid IotDataSinkSaveReqVO updateReqVO); + + /** + * 删除数据目的 + * + * @param id 编号 + */ + void deleteDataSink(Long id); + + /** + * 获得数据目的 + * + * @param id 编号 + * @return 数据目的 + */ + IotDataSinkDO getDataSink(Long id); + + /** + * 获得数据目的分页 + * + * @param pageReqVO 分页查询 + * @return 数据目的分页 + */ + PageResult getDataSinkPage(IotDataSinkPageReqVO pageReqVO); + + /** + * 获取数据目的列表 + * + * @param status 状态,如果为空,则不进行筛选 + * @return 数据目的列表 + */ + List getDataSinkListByStatus(Integer status); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkServiceImpl.java new file mode 100644 index 0000000..fba06ca --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkServiceImpl.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.iot.service.rule.data; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; +import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataSinkMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_BRIDGE_NOT_EXISTS; + +/** + * IoT 数据流转目的 Service 实现类 + * + * @author HUIHUI + */ +@Service +@Validated +public class IotDataSinkServiceImpl implements IotDataSinkService { + + @Resource + private IotDataSinkMapper dataSinkMapper; + + @Override + public Long createDataSink(IotDataSinkSaveReqVO createReqVO) { + IotDataSinkDO dataBridge = BeanUtils.toBean(createReqVO, IotDataSinkDO.class); + dataSinkMapper.insert(dataBridge); + return dataBridge.getId(); + } + + @Override + public void updateDataSink(IotDataSinkSaveReqVO updateReqVO) { + // 校验存在 + validateDataBridgeExists(updateReqVO.getId()); + // 更新 + IotDataSinkDO updateObj = BeanUtils.toBean(updateReqVO, IotDataSinkDO.class); + dataSinkMapper.updateById(updateObj); + } + + @Override + public void deleteDataSink(Long id) { + // 校验存在 + validateDataBridgeExists(id); + // 删除 + dataSinkMapper.deleteById(id); + } + + private void validateDataBridgeExists(Long id) { + if (dataSinkMapper.selectById(id) == null) { + throw exception(DATA_BRIDGE_NOT_EXISTS); + } + } + + @Override + public IotDataSinkDO getDataSink(Long id) { + return dataSinkMapper.selectById(id); + } + + @Override + public PageResult getDataSinkPage(IotDataSinkPageReqVO pageReqVO) { + return dataSinkMapper.selectPage(pageReqVO); + } + + @Override + public List getDataSinkListByStatus(Integer status) { + return dataSinkMapper.selectListByStatus(status); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotRuleSceneDataBridgeAction.java new file mode 100644 index 0000000..459f959 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotRuleSceneDataBridgeAction.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.iot.service.rule.data; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; +import cn.iocoder.yudao.module.iot.service.rule.data.action.IotDataBridgeExecute; +import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotRuleSceneAction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * IoT 数据桥梁的 {@link IotRuleSceneAction} 实现类 + * + * @author 芋道源码 + */ +@Deprecated +@Component +@Slf4j +public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { + + @Resource + private IotDataSinkService dataBridgeService; + @Resource + private List> dataBridgeExecutes; + + @Override + public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception { + // 1.1 如果消息为空,直接返回 + if (message == null) { + return; + } + // 1.2 获得数据桥梁 + Assert.notNull(config.getDataBridgeId(), "数据桥梁编号不能为空"); + IotDataSinkDO dataBridge = dataBridgeService.getDataSink(config.getDataBridgeId()); + if (dataBridge == null || dataBridge.getConfig() == null) { + log.error("[execute][message({}) config({}) 对应的数据桥梁不存在]", message, config); + return; + } + if (CommonStatusEnum.isDisable(dataBridge.getStatus())) { + log.info("[execute][message({}) config({}) 对应的数据桥梁({}) 状态为禁用]", message, config, dataBridge); + return; + } + + // 2. 执行数据桥接操作 + for (IotDataBridgeExecute execute : dataBridgeExecutes) { + execute.execute(message, dataBridge); + } + } + + @Override + public IotRuleSceneActionTypeEnum getType() { + return IotRuleSceneActionTypeEnum.DATA_BRIDGE; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/AbstractCacheableDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/AbstractCacheableDataBridgeExecute.java new file mode 100644 index 0000000..e78a1e4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/AbstractCacheableDataBridgeExecute.java @@ -0,0 +1,114 @@ +package cn.iocoder.yudao.module.iot.service.rule.data.action; + +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.RemovalListener; +import lombok.extern.slf4j.Slf4j; + +import java.time.Duration; + +// TODO @芋艿:数据库 +// TODO @芋艿:mqtt +// TODO @芋艿:tcp +// TODO @芋艿:websocket + +/** + * 带缓存功能的数据桥梁执行器抽象类 + * + * 该类提供了一个通用的缓存机制,用于管理各类数据桥接的生产者(Producer)实例。 + * + * 主要特点: + * - 基于Guava Cache实现高效的生产者实例缓存管理 + * - 自动处理生产者的生命周期(创建、获取、关闭) + * - 支持30分钟未访问自动过期清理机制 + * - 异常处理与日志记录,便于问题排查 + * + * 子类需要实现: + * - initProducer(Config) - 初始化特定类型的生产者实例 + * - closeProducer(Producer) - 关闭生产者实例并释放资源 + * + * @param 配置信息类型,用于初始化生产者 + * @param 生产者类型,负责将数据发送到目标系统 + * @author HUIHUI + */ +@Slf4j +public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridgeExecute { + + /** + * Producer 缓存 + */ + private final LoadingCache PRODUCER_CACHE = CacheBuilder.newBuilder() + .expireAfterAccess(Duration.ofMinutes(30)) // 30 分钟未访问就提前过期 + .removalListener((RemovalListener) notification -> { + Producer producer = notification.getValue(); + if (producer == null) { + return; + } + + try { + closeProducer(producer); + log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已关闭]", notification.getKey()); + } catch (Exception e) { + log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 关闭失败]", notification.getKey(), e); + } + }) + .build(new CacheLoader() { + + @Override + public Producer load(Config config) throws Exception { + try { + Producer producer = initProducer(config); + log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config); + return producer; + } catch (Exception e) { + log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 创建启动失败]", config, e); + throw e; // 抛出异常,触发缓存加载失败机制 + } + } + + }); + + /** + * 获取生产者 + * + * @param config 配置信息 + * @return 生产者对象 + */ + protected Producer getProducer(Config config) throws Exception { + return PRODUCER_CACHE.get(config); + } + + /** + * 初始化生产者 + * + * @param config 配置信息 + * @return 生产者对象 + * @throws Exception 如果初始化失败 + */ + protected abstract Producer initProducer(Config config) throws Exception; + + /** + * 关闭生产者 + * + * @param producer 生产者对象 + */ + protected abstract void closeProducer(Producer producer) throws Exception; + + @Override + @SuppressWarnings({"unchecked"}) + public void execute(IotDeviceMessage message, IotDataSinkDO dataBridge) { + if (ObjUtil.notEqual(dataBridge.getType(), getType())) { + return; + } + try { + execute0(message, (Config) dataBridge.getConfig()); + } catch (Exception e) { + log.error("[execute][桥梁配置 config({}) 对应的 message({}) 发送异常]", dataBridge.getConfig(), message, e); + } + } + +} \ 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/rule/data/action/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataBridgeExecute.java new file mode 100644 index 0000000..e2b69a9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataBridgeExecute.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.iot.service.rule.data.action; + +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; + +/** + * IoT 数据桥梁的执行器 execute 接口 + * + * @author HUIHUI + */ +public interface IotDataBridgeExecute { + + /** + * 获取数据桥梁类型 + * + * @return 数据桥梁类型 + */ + Integer getType(); + + /** + * 执行数据桥梁操作 + * + * @param message 设备消息 + * @param dataBridge 数据桥梁 + */ + @SuppressWarnings({"unchecked"}) + default void execute(IotDeviceMessage message, IotDataSinkDO dataBridge) throws Exception { + // 1.1 校验数据桥梁类型 + if (!getType().equals(dataBridge.getType())) { + return; + } + + // 1.2 执行对应的数据桥梁发送消息 + execute0(message, (Config) dataBridge.getConfig()); + } + + /** + * 【真正】执行数据桥梁操作 + * + * @param message 设备消息 + * @param config 桥梁配置 + */ + void execute0(IotDeviceMessage message, Config config) throws Exception; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataBridgeExecute.java new file mode 100644 index 0000000..7229a23 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataBridgeExecute.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.iot.service.rule.data.action; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataBridgeHttpConfig; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.HashMap; +import java.util.Map; + +/** + * Http 的 {@link IotDataBridgeExecute} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class IotHttpDataBridgeExecute implements IotDataBridgeExecute { + + @Resource + private RestTemplate restTemplate; + + @Override + public Integer getType() { + return IotDataSinkTypeEnum.HTTP.getType(); + } + + @Override + @SuppressWarnings({"unchecked", "deprecation"}) + public void execute0(IotDeviceMessage message, IotDataBridgeHttpConfig config) { + String url = null; + HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase()); + HttpEntity requestEntity = null; + ResponseEntity responseEntity = null; + try { + // 1.1 构建 Header + HttpHeaders headers = new HttpHeaders(); + if (CollUtil.isNotEmpty(config.getHeaders())) { + config.getHeaders().putAll(config.getHeaders()); + } + // TODO @puhui999:@yunai:可能需要通过设备查询到租户,然后 set +// headers.add(HEADER_TENANT_ID, message.getTenantId().toString()); + // 1.2 构建 URL + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(config.getUrl()); + if (CollUtil.isNotEmpty(config.getQuery())) { + config.getQuery().forEach(uriBuilder::queryParam); + } + // 1.3 构建请求体 + if (method == HttpMethod.GET) { + uriBuilder.queryParam("message", HttpUtils.encodeUtf8(JsonUtils.toJsonString(message))); + url = uriBuilder.build().toUriString(); + requestEntity = new HttpEntity<>(headers); + } else { + url = uriBuilder.build().toUriString(); + Map requestBody = JsonUtils.parseObject(config.getBody(), Map.class); + if (requestBody == null) { + requestBody = new HashMap<>(); + } + requestBody.put("message", message); + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); + requestEntity = new HttpEntity<>(JsonUtils.toJsonString(requestBody), headers); + } + + // 2.1 发送请求 + responseEntity = restTemplate.exchange(url, method, requestEntity, String.class); + // 2.2 记录日志 + if (responseEntity.getStatusCode().is2xxSuccessful()) { + log.info("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求成功({})]", + message, config, url, method, requestEntity, responseEntity); + } else { + log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求失败({})]", + message, config, url, method, requestEntity, responseEntity); + } + } catch (Exception e) { + log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求异常({})]", + message, config, url, method, requestEntity, responseEntity, e); + } + } + +} \ 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/rule/data/action/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotKafkaMQDataBridgeExecute.java new file mode 100644 index 0000000..9ee08b0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotKafkaMQDataBridgeExecute.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.iot.service.rule.data.action; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataBridgeKafkaMQConfig; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Kafka 的 {@link IotDataBridgeExecute} 实现类 + * + * @author HUIHUI + */ +@ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate") +@Component +@Slf4j +public class IotKafkaMQDataBridgeExecute extends + AbstractCacheableDataBridgeExecute> { + + private static final Duration SEND_TIMEOUT = Duration.ofMillis(10000); // 10 秒超时时间 + + @Override + public Integer getType() { + return IotDataSinkTypeEnum.KAFKA.getType(); + } + + @Override + public void execute0(IotDeviceMessage message, IotDataBridgeKafkaMQConfig config) throws Exception { + // 1. 获取或创建 KafkaTemplate + KafkaTemplate kafkaTemplate = getProducer(config); + + // 2. 发送消息并等待结果 + kafkaTemplate.send(config.getTopic(), message.toString()) + .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS); // 添加超时等待 + log.info("[execute0][message({}) 发送成功]", message); + } + + @Override + protected KafkaTemplate initProducer(IotDataBridgeKafkaMQConfig config) { + // 1.1 构建生产者配置 + Map props = new HashMap<>(); + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers()); + props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + // 1.2 如果配置了认证信息 + if (config.getUsername() != null && config.getPassword() != null) { + props.put("security.protocol", "SASL_PLAINTEXT"); + props.put("sasl.mechanism", "PLAIN"); + props.put("sasl.jaas.config", + "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"" + + config.getUsername() + "\" password=\"" + config.getPassword() + "\";"); + } + // 1.3 如果启用 SSL + if (Boolean.TRUE.equals(config.getSsl())) { + props.put("security.protocol", "SSL"); + } + + // 2. 创建 KafkaTemplate + DefaultKafkaProducerFactory producerFactory = new DefaultKafkaProducerFactory<>(props); + return new KafkaTemplate<>(producerFactory); + } + + @Override + protected void closeProducer(KafkaTemplate producer) { + producer.destroy(); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRabbitMQDataBridgeExecute.java new file mode 100644 index 0000000..597bbf3 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRabbitMQDataBridgeExecute.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.iot.service.rule.data.action; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataBridgeRabbitMQConfig; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; + +/** + * RabbitMQ 的 {@link IotDataBridgeExecute} 实现类 + * + * @author HUIHUI + */ +@ConditionalOnClass(name = "com.rabbitmq.client.Channel") +@Component +@Slf4j +public class IotRabbitMQDataBridgeExecute extends + AbstractCacheableDataBridgeExecute { + + + @Override + public Integer getType() { + return IotDataSinkTypeEnum.RABBITMQ.getType(); + } + + @Override + public void execute0(IotDeviceMessage message, IotDataBridgeRabbitMQConfig config) throws Exception { + // 1. 获取或创建 Channel + Channel channel = getProducer(config); + + // 2.1 声明交换机、队列和绑定关系 + channel.exchangeDeclare(config.getExchange(), "direct", true); + channel.queueDeclare(config.getQueue(), true, false, false, null); + channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey()); + + // 2.2 发送消息 + channel.basicPublish(config.getExchange(), config.getRoutingKey(), null, + message.toString().getBytes(StandardCharsets.UTF_8)); + log.info("[executeRabbitMQ][message({}) config({}) 发送成功]", message, config); + } + + @Override + @SuppressWarnings("resource") + protected Channel initProducer(IotDataBridgeRabbitMQConfig config) throws Exception { + // 1. 创建连接工厂 + ConnectionFactory factory = new ConnectionFactory(); + factory.setHost(config.getHost()); + factory.setPort(config.getPort()); + factory.setVirtualHost(config.getVirtualHost()); + factory.setUsername(config.getUsername()); + factory.setPassword(config.getPassword()); + + // 2. 创建连接 + Connection connection = factory.newConnection(); + + // 3. 创建信道 + return connection.createChannel(); + } + + @Override + protected void closeProducer(Channel channel) throws Exception { + if (channel.isOpen()) { + channel.close(); + } + Connection connection = channel.getConnection(); + if (connection.isOpen()) { + connection.close(); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisStreamDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisStreamDataBridgeExecute.java new file mode 100644 index 0000000..7906bec --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisStreamDataBridgeExecute.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.iot.service.rule.data.action; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataBridgeRedisStreamConfig; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; +import lombok.extern.slf4j.Slf4j; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.SingleServerConfig; +import org.redisson.spring.data.connection.RedissonConnectionFactory; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.stream.ObjectRecord; +import org.springframework.data.redis.connection.stream.StreamRecords; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.stereotype.Component; + +/** + * Redis Stream 的 {@link IotDataBridgeExecute} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class IotRedisStreamDataBridgeExecute extends + AbstractCacheableDataBridgeExecute> { + + @Override + public Integer getType() { + return IotDataSinkTypeEnum.REDIS_STREAM.getType(); + } + + @Override + public void execute0(IotDeviceMessage message, IotDataBridgeRedisStreamConfig config) throws Exception { + // 1. 获取 RedisTemplate + RedisTemplate redisTemplate = getProducer(config); + + // 2. 创建并发送 Stream 记录 + ObjectRecord record = StreamRecords.newRecord() + .ofObject(message).withStreamKey(config.getTopic()); + String recordId = String.valueOf(redisTemplate.opsForStream().add(record)); + log.info("[executeRedisStream][消息发送成功] messageId: {}, config: {}", recordId, config); + } + + @Override + protected RedisTemplate initProducer(IotDataBridgeRedisStreamConfig config) { + // 1.1 创建 Redisson 配置 + Config redissonConfig = new Config(); + SingleServerConfig serverConfig = redissonConfig.useSingleServer() + .setAddress("redis://" + config.getHost() + ":" + config.getPort()) + .setDatabase(config.getDatabase()); + // 1.2 设置密码(如果有) + if (StrUtil.isNotBlank(config.getPassword())) { + serverConfig.setPassword(config.getPassword()); + } + + // 创建 RedisTemplate 并配置 + RedissonClient redisson = Redisson.create(redissonConfig); + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(new RedissonConnectionFactory(redisson)); + // 设置序列化器 + template.setKeySerializer(RedisSerializer.string()); + template.setHashKeySerializer(RedisSerializer.string()); + template.setValueSerializer(RedisSerializer.json()); + template.setHashValueSerializer(RedisSerializer.json()); + template.afterPropertiesSet(); + return template; + } + + @Override + protected void closeProducer(RedisTemplate producer) throws Exception { + RedisConnectionFactory factory = producer.getConnectionFactory(); + if (factory != null) { + ((RedissonConnectionFactory) factory).destroy(); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRocketMQDataBridgeExecute.java new file mode 100644 index 0000000..910b644 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRocketMQDataBridgeExecute.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.iot.service.rule.data.action; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataBridgeRocketMQConfig; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.remoting.common.RemotingHelper; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.stereotype.Component; + +/** + * RocketMQ 的 {@link IotDataBridgeExecute} 实现类 + * + * @author HUIHUI + */ +@ConditionalOnClass(name = "org.apache.rocketmq.client.producer.DefaultMQProducer") +@Component +@Slf4j +public class IotRocketMQDataBridgeExecute extends + AbstractCacheableDataBridgeExecute { + + @Override + public Integer getType() { + return IotDataSinkTypeEnum.ROCKETMQ.getType(); + } + + @Override + public void execute0(IotDeviceMessage message, IotDataBridgeRocketMQConfig config) throws Exception { + // 1. 获取或创建 Producer + DefaultMQProducer producer = getProducer(config); + + // 2.1 创建消息对象,指定Topic、Tag和消息体 + Message msg = new Message( + config.getTopic(), + config.getTags(), + message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET) + ); + // 2.2 发送同步消息并处理结果 + SendResult sendResult = producer.send(msg); + // 2.3 处理发送结果 + if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { + log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", message, config, sendResult); + } else { + log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", message, config, sendResult); + } + } + + @Override + protected DefaultMQProducer initProducer(IotDataBridgeRocketMQConfig config) throws Exception { + DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); + producer.setNamesrvAddr(config.getNameServer()); + producer.start(); + return producer; + } + + @Override + protected void closeProducer(DefaultMQProducer producer) { + producer.shutdown(); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneService.java new file mode 100644 index 0000000..0bf43d3 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneService.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * IoT 规则场景 Service 接口 + * + * @author 芋道源码 + */ +public interface IotRuleSceneService { + + /** + * 创建场景联动 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createRuleScene(@Valid IotRuleSceneSaveReqVO createReqVO); + + /** + * 更新场景联动 + * + * @param updateReqVO 更新信息 + */ + void updateRuleScene(@Valid IotRuleSceneSaveReqVO updateReqVO); + + /** + * 删除场景联动 + * + * @param id 编号 + */ + void deleteRuleScene(Long id); + + /** + * 获得场景联动 + * + * @param id 编号 + * @return 场景联动 + */ + IotRuleSceneDO getRuleScene(Long id); + + /** + * 获得场景联动分页 + * + * @param pageReqVO 分页查询 + * @return 场景联动分页 + */ + PageResult getRuleScenePage(IotRuleScenePageReqVO pageReqVO); + + /** + * 【缓存】获得指定设备的场景列表 + * + * @param productKey 产品 Key + * @param deviceName 设备名称 + * @return 场景列表 + */ + List getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName); + + /** + * 基于 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 场景,执行规则场景 + * + * @param message 消息 + */ + void executeRuleSceneByDevice(IotDeviceMessage message); + + /** + * 基于 {@link IotRuleSceneTriggerTypeEnum#TIMER} 场景,执行规则场景 + * + * @param id 场景编号 + */ + void executeRuleSceneByTimer(Long id); + + /** + * TODO 芋艿:测试方法,需要删除 + */ + void test(); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneServiceImpl.java new file mode 100644 index 0000000..b422e0d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneServiceImpl.java @@ -0,0 +1,492 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.text.CharPool; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionOperatorEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; +import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; +import cn.iocoder.yudao.module.iot.job.rule.IotRuleSceneJob; +import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotRuleSceneAction; +import jakarta.annotation.Resource; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerKey; +import org.quartz.impl.StdSchedulerFactory; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RULE_SCENE_NOT_EXISTS; + +/** + * IoT 规则场景 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class IotRuleSceneServiceImpl implements IotRuleSceneService { + + @Resource + private IotRuleSceneMapper ruleSceneMapper; + + @Resource + private List ruleSceneActions; + + @Resource(name = "iotSchedulerManager") + private IotSchedulerManager schedulerManager; + + @Override + public Long createRuleScene(IotRuleSceneSaveReqVO createReqVO) { + // 插入 + IotRuleSceneDO ruleScene = BeanUtils.toBean(createReqVO, IotRuleSceneDO.class); + ruleSceneMapper.insert(ruleScene); + // 返回 + return ruleScene.getId(); + } + + @Override + public void updateRuleScene(IotRuleSceneSaveReqVO updateReqVO) { + // 校验存在 + validateRuleSceneExists(updateReqVO.getId()); + // 更新 + IotRuleSceneDO updateObj = BeanUtils.toBean(updateReqVO, IotRuleSceneDO.class); + ruleSceneMapper.updateById(updateObj); + } + + @Override + public void deleteRuleScene(Long id) { + // 校验存在 + validateRuleSceneExists(id); + // 删除 + ruleSceneMapper.deleteById(id); + } + + private void validateRuleSceneExists(Long id) { + if (ruleSceneMapper.selectById(id) == null) { + throw exception(RULE_SCENE_NOT_EXISTS); + } + } + + @Override + public IotRuleSceneDO getRuleScene(Long id) { + return ruleSceneMapper.selectById(id); + } + + @Override + public PageResult getRuleScenePage(IotRuleScenePageReqVO pageReqVO) { + return ruleSceneMapper.selectPage(pageReqVO); + } + + + // TODO 芋艿,缓存待实现 + @Override + @TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略 + public List getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) { + if (true) { + IotRuleSceneDO ruleScene01 = new IotRuleSceneDO(); + ruleScene01.setTriggers(CollUtil.newArrayList()); + IotRuleSceneDO.TriggerConfig trigger01 = new IotRuleSceneDO.TriggerConfig(); + trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType()); + trigger01.setConditions(CollUtil.newArrayList()); + // 属性 + IotRuleSceneDO.TriggerCondition condition01 = new IotRuleSceneDO.TriggerCondition(); + condition01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); + condition01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier()); + condition01.setParameters(CollUtil.newArrayList()); +// IotRuleSceneDO.TriggerConditionParameter parameter010 = new IotRuleSceneDO.TriggerConditionParameter(); +// parameter010.setIdentifier("width"); +// parameter010.setOperator(IotRuleSceneConditionOperatorEnum.EQUALS.getOperator()); +// parameter010.setValue("abc"); +// condition01.getParameters().add(parameter010); + IotRuleSceneDO.TriggerConditionParameter parameter011 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter011.setIdentifier("width"); + parameter011.setOperator(IotRuleSceneConditionOperatorEnum.EQUALS.getOperator()); + parameter011.setValue("1"); + condition01.getParameters().add(parameter011); + IotRuleSceneDO.TriggerConditionParameter parameter012 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter012.setIdentifier("width"); + parameter012.setOperator(IotRuleSceneConditionOperatorEnum.NOT_EQUALS.getOperator()); + parameter012.setValue("2"); + condition01.getParameters().add(parameter012); + IotRuleSceneDO.TriggerConditionParameter parameter013 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter013.setIdentifier("width"); + parameter013.setOperator(IotRuleSceneConditionOperatorEnum.GREATER_THAN.getOperator()); + parameter013.setValue("0"); + condition01.getParameters().add(parameter013); + IotRuleSceneDO.TriggerConditionParameter parameter014 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter014.setIdentifier("width"); + parameter014.setOperator(IotRuleSceneConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator()); + parameter014.setValue("0"); + condition01.getParameters().add(parameter014); + IotRuleSceneDO.TriggerConditionParameter parameter015 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter015.setIdentifier("width"); + parameter015.setOperator(IotRuleSceneConditionOperatorEnum.LESS_THAN.getOperator()); + parameter015.setValue("2"); + condition01.getParameters().add(parameter015); + IotRuleSceneDO.TriggerConditionParameter parameter016 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter016.setIdentifier("width"); + parameter016.setOperator(IotRuleSceneConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator()); + parameter016.setValue("2"); + condition01.getParameters().add(parameter016); + IotRuleSceneDO.TriggerConditionParameter parameter017 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter017.setIdentifier("width"); + parameter017.setOperator(IotRuleSceneConditionOperatorEnum.IN.getOperator()); + parameter017.setValue("1,2,3"); + condition01.getParameters().add(parameter017); + IotRuleSceneDO.TriggerConditionParameter parameter018 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter018.setIdentifier("width"); + parameter018.setOperator(IotRuleSceneConditionOperatorEnum.NOT_IN.getOperator()); + parameter018.setValue("0,2,3"); + condition01.getParameters().add(parameter018); + IotRuleSceneDO.TriggerConditionParameter parameter019 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter019.setIdentifier("width"); + parameter019.setOperator(IotRuleSceneConditionOperatorEnum.BETWEEN.getOperator()); + parameter019.setValue("1,3"); + condition01.getParameters().add(parameter019); + IotRuleSceneDO.TriggerConditionParameter parameter020 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter020.setIdentifier("width"); + parameter020.setOperator(IotRuleSceneConditionOperatorEnum.NOT_BETWEEN.getOperator()); + parameter020.setValue("2,3"); + condition01.getParameters().add(parameter020); + trigger01.getConditions().add(condition01); + // 状态 + IotRuleSceneDO.TriggerCondition condition02 = new IotRuleSceneDO.TriggerCondition(); + condition02.setType(IotDeviceMessageTypeEnum.STATE.getType()); + condition02.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()); + condition02.setParameters(CollUtil.newArrayList()); + trigger01.getConditions().add(condition02); + // 事件 + IotRuleSceneDO.TriggerCondition condition03 = new IotRuleSceneDO.TriggerCondition(); + condition03.setType(IotDeviceMessageTypeEnum.EVENT.getType()); + condition03.setIdentifier("xxx"); + condition03.setParameters(CollUtil.newArrayList()); + IotRuleSceneDO.TriggerConditionParameter parameter030 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter030.setIdentifier("width"); + parameter030.setOperator(IotRuleSceneConditionOperatorEnum.EQUALS.getOperator()); + parameter030.setValue("1"); + trigger01.getConditions().add(condition03); + ruleScene01.getTriggers().add(trigger01); + // 动作 + ruleScene01.setActions(CollUtil.newArrayList()); + // 设备控制 + IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig(); + action01.setType(IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET.getType()); + IotRuleSceneDO.ActionDeviceControl actionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl(); + actionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT"); + actionDeviceControl01.setDeviceNames(ListUtil.of("small")); + actionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); + actionDeviceControl01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()); + actionDeviceControl01.setData(MapUtil.builder() + .put("power", 1) + .put("color", "red") + .build()); + action01.setDeviceControl(actionDeviceControl01); +// ruleScene01.getActions().add(action01); // TODO 芋艿:先不测试了 + // 数据桥接(http) + IotRuleSceneDO.ActionConfig action02 = new IotRuleSceneDO.ActionConfig(); + action02.setType(IotRuleSceneActionTypeEnum.DATA_BRIDGE.getType()); + action02.setDataBridgeId(1L); + ruleScene01.getActions().add(action02); + return ListUtil.toList(ruleScene01); + } + + List list = ruleSceneMapper.selectList(); + // TODO @芋艿:需要考虑开启状态 + return filterList(list, ruleScene -> { + for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) { + if (ObjUtil.notEqual(trigger.getProductKey(), productKey)) { + continue; + } + if (CollUtil.isEmpty(trigger.getDeviceNames()) // 无设备名称限制 + || trigger.getDeviceNames().contains(deviceName)) { // 包含设备名称 + return true; + } + } + return false; + }); + } + + @Override + public void executeRuleSceneByDevice(IotDeviceMessage message) { + // TODO @芋艿:这里的 tenantId,通过设备获取; +// TenantUtils.execute(message.getTenantId(), () -> { +// // 1. 获得设备匹配的规则场景 +// List ruleScenes = getMatchedRuleSceneListByMessage(message); +// if (CollUtil.isEmpty(ruleScenes)) { +// return; +// } +// +// // 2. 执行规则场景 +// executeRuleSceneAction(message, ruleScenes); +// }); + } + + @Override + public void executeRuleSceneByTimer(Long id) { + // 1.1 获得规则场景 +// IotRuleSceneDO scene = TenantUtils.executeIgnore(() -> ruleSceneMapper.selectById(id)); + // TODO @芋艿:这里,临时测试,后续删除。 + IotRuleSceneDO scene = new IotRuleSceneDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); + if (true) { + scene.setTenantId(1L); + IotRuleSceneDO.TriggerConfig triggerConfig = new IotRuleSceneDO.TriggerConfig(); + triggerConfig.setType(IotRuleSceneTriggerTypeEnum.TIMER.getType()); + scene.setTriggers(ListUtil.toList(triggerConfig)); + // 动作 + IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig(); + action01.setType(IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET.getType()); + IotRuleSceneDO.ActionDeviceControl iotRuleSceneActionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl(); + iotRuleSceneActionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT"); + iotRuleSceneActionDeviceControl01.setDeviceNames(ListUtil.of("small")); + iotRuleSceneActionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); + iotRuleSceneActionDeviceControl01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()); + iotRuleSceneActionDeviceControl01.setData(MapUtil.builder() + .put("power", 1) + .put("color", "red") + .build()); + action01.setDeviceControl(iotRuleSceneActionDeviceControl01); + scene.setActions(ListUtil.toList(action01)); + } + if (scene == null) { + log.error("[executeRuleSceneByTimer][规则场景({}) 不存在]", id); + return; + } + if (CommonStatusEnum.isDisable(scene.getStatus())) { + log.info("[executeRuleSceneByTimer][规则场景({}) 已被禁用]", id); + return; + } + // 1.2 判断是否有定时触发器,避免脏数据 + IotRuleSceneDO.TriggerConfig config = CollUtil.findOne(scene.getTriggers(), + trigger -> ObjUtil.equals(trigger.getType(), IotRuleSceneTriggerTypeEnum.TIMER.getType())); + if (config == null) { + log.error("[executeRuleSceneByTimer][规则场景({}) 不存在定时触发器]", scene); + return; + } + + // 2. 执行规则场景 + TenantUtils.execute(scene.getTenantId(), + () -> executeRuleSceneAction(null, ListUtil.toList(scene))); + } + + /** + * 基于消息,获得匹配的规则场景列表 + * + * @param message 设备消息 + * @return 规则场景列表 + */ + private List getMatchedRuleSceneListByMessage(IotDeviceMessage message) { + // 1. 匹配设备 + // TODO @芋艿:可能需要 getSelf(); 缓存 + List ruleScenes = null; + // TODO @芋艿:这里需要适配 +// List ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache( +// message.getProductKey(), message.getDeviceName()); + if (CollUtil.isEmpty(ruleScenes)) { + return ruleScenes; + } + + // 2. 匹配 trigger 触发器的条件 + return filterList(ruleScenes, ruleScene -> { + for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) { + // 2.1 非设备触发,不匹配 + if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) { + return false; + } + // TODO 芋艿:产品、设备的匹配,要不要这里在做一次???貌似和 1. 部分重复了 + // 2.2 条件为空,说明没有匹配的条件,因此不匹配 + if (CollUtil.isEmpty(trigger.getConditions())) { + return false; + } + // 2.3 多个条件,只需要满足一个即可 + IotRuleSceneDO.TriggerCondition matchedCondition = CollUtil.findOne(trigger.getConditions(), condition -> { + // TODO @芋艿:这里的逻辑,需要适配 +// if (ObjUtil.notEqual(message.getType(), condition.getType()) +// || ObjUtil.notEqual(message.getIdentifier(), condition.getIdentifier())) { +// return false; +// } + // 多个条件参数,必须全部满足。所以,下面的逻辑就是找到一个不满足的条件参数 + IotRuleSceneDO.TriggerConditionParameter notMatchedParameter = CollUtil.findOne(condition.getParameters(), + parameter -> !isTriggerConditionParameterMatched(message, parameter, ruleScene, trigger)); + return notMatchedParameter == null; + }); + if (matchedCondition == null) { + return false; + } + log.info("[getMatchedRuleSceneList][消息({}) 匹配到规则场景编号({}) 的触发器({})]", message, ruleScene.getId(), trigger); + return true; + } + return false; + }); + } + + // TODO @芋艿:【可优化】可以考虑增加下单测,边界太多了。 + + /** + * 判断触发器的条件参数是否匹配 + * + * @param message 设备消息 + * @param parameter 触发器条件参数 + * @param ruleScene 规则场景(用于日志,无其它作用) + * @param trigger 触发器(用于日志,无其它作用) + * @return 是否匹配 + */ + @SuppressWarnings({"unchecked", "DataFlowIssue"}) + private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter, + IotRuleSceneDO ruleScene, IotRuleSceneDO.TriggerConfig trigger) { + // 1.1 校验操作符是否合法 + IotRuleSceneConditionOperatorEnum operator = + IotRuleSceneConditionOperatorEnum.operatorOf(parameter.getOperator()); + if (operator == null) { + log.error("[isTriggerConditionParameterMatched][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]", + ruleScene.getId(), trigger, parameter.getOperator()); + return false; + } + // 1.2 校验消息是否包含对应的值 + String messageValue = MapUtil.getStr((Map) message.getData(), parameter.getIdentifier()); + if (messageValue == null) { + return false; + } + + // 2.1 构建 Spring 表达式的变量 + Map springExpressionVariables = new HashMap<>(); + try { + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue); + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE, parameter.getValue()); + List parameterValues = StrUtil.splitTrim(parameter.getValue(), CharPool.COMMA); + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues); + // 特殊:解决数字的比较。因为 Spring 是基于它的 compareTo 方法,对数字的比较存在问题! + if (ObjectUtils.equalsAny(operator, IotRuleSceneConditionOperatorEnum.BETWEEN, + IotRuleSceneConditionOperatorEnum.NOT_BETWEEN, + IotRuleSceneConditionOperatorEnum.GREATER_THAN, + IotRuleSceneConditionOperatorEnum.GREATER_THAN_OR_EQUALS, + IotRuleSceneConditionOperatorEnum.LESS_THAN, + IotRuleSceneConditionOperatorEnum.LESS_THAN_OR_EQUALS) + && NumberUtil.isNumber(messageValue) + && NumberUtils.isAllNumber(parameterValues)) { + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, + NumberUtil.parseDouble(messageValue)); + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE, + NumberUtil.parseDouble(parameter.getValue())); + springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, + convertList(parameterValues, NumberUtil::parseDouble)); + } + // 2.2 计算 Spring 表达式 + return (Boolean) SpringExpressionUtils.parseExpression(operator.getSpringExpression(), springExpressionVariables); + } catch (Exception e) { + log.error("[isTriggerConditionParameterMatched][消息({}) 规则场景编号({}) 的触发器({}) 的匹配表达式({}/{}) 计算异常]", + message, ruleScene.getId(), trigger, operator, springExpressionVariables, e); + return false; + } + } + + /** + * 执行规则场景的动作 + * + * @param message 设备消息 + * @param ruleScenes 规则场景列表 + */ + private void executeRuleSceneAction(IotDeviceMessage message, List ruleScenes) { + // 1. 遍历规则场景 + ruleScenes.forEach(ruleScene -> { + // 2. 遍历规则场景的动作 + ruleScene.getActions().forEach(actionConfig -> { + // 3.1 获取对应的动作 Action 数组 + List actions = filterList(ruleSceneActions, + action -> action.getType().getType().equals(actionConfig.getType())); + if (CollUtil.isEmpty(actions)) { + return; + } + // 3.2 执行动作 + actions.forEach(action -> { + try { + action.execute(message, actionConfig); + log.info("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]", + message, ruleScene.getId(), actionConfig); + } catch (Exception e) { + log.error("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 执行异常]", + message, ruleScene.getId(), actionConfig, e); + } + }); + }); + }); + } + + @Override + @SneakyThrows + public void test() { + // TODO @芋艿:测试思路代码,记得删除!!! + // 1. Job 类:IotRuleSceneJob DONE + // 2. 参数:id DONE + // 3. jobHandlerName:IotRuleSceneJob + id DONE + + // 新增:addJob + // 修改:不存在 addJob、存在 updateJob + // 有 + 禁用:1)存在、停止;2)不存在:不处理;TODO 测试:直接暂停,是否可以???(结论:可以)pauseJob + // 有 + 开启:1)存在,更新;2)不存在,新增;结论:使用 save(addOrUpdateJob) + // 无 + 禁用、开启:1)存在,删除;TODO 测试:直接删除???(结论:可以)deleteJob + + // + if (false) { + Long id = 1L; + Map jobDataMap = IotRuleSceneJob.buildJobDataMap(id); + schedulerManager.addOrUpdateJob(IotRuleSceneJob.class, + IotRuleSceneJob.buildJobName(id), + "0/10 * * * * ?", + jobDataMap); + } + if (false) { + Long id = 1L; + schedulerManager.pauseJob(IotRuleSceneJob.buildJobName(id)); + } + if (true) { + Long id = 1L; + schedulerManager.deleteJob(IotRuleSceneJob.buildJobName(id)); + } + } + + public static void main2(String[] args) throws SchedulerException { +// System.out.println(QuartzJobBean.class); + Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); + scheduler.start(); + + String jobHandlerName = "123"; + // 暂停 Trigger 对象 + scheduler.pauseTrigger(new TriggerKey(jobHandlerName)); + // 取消并删除 Job 调度 + scheduler.unscheduleJob(new TriggerKey(jobHandlerName)); + scheduler.deleteJob(new JobKey(jobHandlerName)); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneAction.java new file mode 100644 index 0000000..3d24384 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneAction.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.action; + +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; + +import javax.annotation.Nullable; + +/** + * IoT 规则场景的场景执行器接口 + * + * @author 芋道源码 + */ +public interface IotRuleSceneAction { + + // TODO @芋艿:groovy 或者 javascript 实现数据的转换;可以考虑基于 hutool 的 ScriptUtil 做 + + /** + * 执行场景 + * + * @param message 消息,允许空 + * 1. 空的情况:定时触发 + * 2. 非空的情况:设备触发 + * @param config 配置 + */ + void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception; + + /** + * 获得类型 + * + * @return 类型 + */ + IotRuleSceneActionTypeEnum getType(); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneAlertAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneAlertAction.java new file mode 100644 index 0000000..fd55cb8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneAlertAction.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.action; + +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; +import org.springframework.stereotype.Component; + +import javax.annotation.Nullable; + +/** + * IoT 告警的 {@link IotRuleSceneAction} 实现类 + * + * @author 芋道源码 + */ +@Component +public class IotRuleSceneAlertAction implements IotRuleSceneAction { + + @Override + public void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { + // TODO @芋艿:待实现 + } + + @Override + public IotRuleSceneActionTypeEnum getType() { + return IotRuleSceneActionTypeEnum.ALERT; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneDeviceControlAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneDeviceControlAction.java new file mode 100644 index 0000000..e7977e7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotRuleSceneDeviceControlAction.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.action; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * IoT 设备控制的 {@link IotRuleSceneAction} 实现类 + * + * @author 芋道源码 + */ +@Component +@Slf4j +public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction { + + @Resource + private IotDeviceService deviceService; + @Resource + private IotDeviceMessageService deviceMessageService; + + @Override + public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { + IotRuleSceneDO.ActionDeviceControl control = config.getDeviceControl(); + Assert.notNull(control, "设备控制配置不能为空"); + // 遍历每个设备,下发消息 + control.getDeviceNames().forEach(deviceName -> { + IotDeviceDO device = deviceService.getDeviceFromCache(control.getProductKey(), deviceName); + if (device == null) { + log.error("[execute][message({}) config({}) 对应的设备不存在]", message, config); + return; + } + try { + // TODO @芋艿:@puhui999:这块可能要改,从 type => method + IotDeviceMessage downstreamMessage = deviceMessageService.sendDeviceMessage(IotDeviceMessage.requestOf( + control.getType() + control.getIdentifier(), control.getData()).setDeviceId(device.getId())); + log.info("[execute][message({}) config({}) 下发消息({})成功]", message, config, downstreamMessage); + } catch (Exception e) { + log.error("[execute][message({}) config({}) 下发消息失败]", message, config, e); + } + }); + } + + @Override + public IotRuleSceneActionTypeEnum getType() { + return IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java index f1cf3b7..802cc6a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java @@ -1,9 +1,10 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.*; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleSinkDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.*; +import cn.iocoder.yudao.module.iot.service.rule.data.action.*; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -129,7 +130,7 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { // 3. 执行测试 log.info("[testHttpDataBridge][执行HTTP数据桥接测试]"); - httpDataBridgeExecute.execute(message, new IotDataRuleSinkDO() + httpDataBridgeExecute.execute(message, new IotDataSinkDO() .setType(httpDataBridgeExecute.getType()).setConfig(config)); } @@ -144,10 +145,10 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { private void executeAndVerifyCache(IotDataBridgeExecute action, IotDataBridgeAbstractConfig config, String type) throws Exception { log.info("[test{}DataBridge][第一次执行,应该会创建新的 producer]", type); - action.execute(message, new IotDataRuleSinkDO().setType(action.getType()).setConfig(config)); + action.execute(message, new IotDataSinkDO().setType(action.getType()).setConfig(config)); log.info("[test{}DataBridge][第二次执行,应该会复用缓存的 producer]", type); - action.execute(message, new IotDataRuleSinkDO().setType(action.getType()).setConfig(config)); + action.execute(message, new IotDataSinkDO().setType(action.getType()).setConfig(config)); } } -- libgit2 0.21.4