Explorar o código

开发数据规则引擎基础功能

james hai 6 días
pai
achega
80f2d88851
Modificáronse 100 ficheiros con 8889 adicións e 1 borrados
  1. 2 1
      pom.xml
  2. 17 0
      service-rule/pom.xml
  3. 26 0
      service-rule/service-rule-api/pom.xml
  4. 84 0
      service-rule/service-rule-biz/pom.xml
  5. 44 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/RuoYiSystemApplication.java
  6. 113 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/cache/DeviceTriggerIncludeMinuteCache.java
  7. 32 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/cache/RuleEngineCache.java
  8. 132 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/config/CronTaskManager.java
  9. 22 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/constant/DateTimeConstants.java
  10. 36 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/constant/RegExpConstants.java
  11. 108 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/MybatisGeneratorUtils.java
  12. 21 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/BaseBuildController.java
  13. 21 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/BaseSpaceController.java
  14. 21 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/DmpDeviceController.java
  15. 21 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/DmpProductAttributeController.java
  16. 21 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/DmpProductController.java
  17. 116 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/RuleEngineController.java
  18. 53 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/RuleEngineLogController.java
  19. 155 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/crons/TriggerCronTask.java
  20. 212 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/BaseBuild.java
  21. 91 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/BaseSpace.java
  22. 156 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/DmpDevice.java
  23. 166 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/DmpProduct.java
  24. 142 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/DmpProductAttribute.java
  25. 40 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/RuleEngine.java
  26. 32 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/RuleEngineCondition.java
  27. 28 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/RuleEngineCron.java
  28. 30 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/RuleEngineDevice.java
  29. 40 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/RuleEngineLog.java
  30. 62 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/engine/RuleConditionEvaluator.java
  31. 30 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/enums/ActionTypeEnum.java
  32. 28 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/enums/ConstraintTypeEnum.java
  33. 29 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/enums/TimeTypeEnum.java
  34. 27 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/enums/TriggerTypeEnum.java
  35. 22 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/enums/TriggerValueTypeEnum.java
  36. 25 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/exception/BizException.java
  37. 40 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/jobs/CommonJob.java
  38. 182 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/jobs/ConsumptionJob.java
  39. 94 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/listeners/CommonListener.java
  40. 16 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/BaseBuildMapper.java
  41. 16 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/BaseSpaceMapper.java
  42. 16 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/DmpDeviceMapper.java
  43. 16 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/DmpProductAttributeMapper.java
  44. 16 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/DmpProductMapper.java
  45. 14 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/RuleEngineConditionMapper.java
  46. 18 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/RuleEngineCronMapper.java
  47. 19 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/RuleEngineDeviceMapper.java
  48. 15 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/RuleEngineLogMapper.java
  49. 19 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/RuleEngineMapper.java
  50. 44 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mq/RuleReportConsumer.java
  51. 84 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mq/RuleTriggerContext.java
  52. 23 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/mq/vo/DeviceReportPayload.java
  53. 78 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/schedule/RuleEngineCronScheduler.java
  54. 16 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/BaseBuildService.java
  55. 16 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/BaseSpaceService.java
  56. 16 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/DmpDeviceService.java
  57. 16 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/DmpProductAttributeService.java
  58. 16 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/DmpProductService.java
  59. 13 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineConditionService.java
  60. 15 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineCronService.java
  61. 13 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineDetailService.java
  62. 15 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineDeviceService.java
  63. 14 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineLogService.java
  64. 56 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineService.java
  65. 20 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/BaseBuildServiceImpl.java
  66. 20 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/BaseSpaceServiceImpl.java
  67. 20 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/DmpDeviceServiceImpl.java
  68. 20 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/DmpProductAttributeServiceImpl.java
  69. 20 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/DmpProductServiceImpl.java
  70. 31 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineConditionServiceImpl.java
  71. 22 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineCronServiceImpl.java
  72. 109 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineDetailServiceImpl.java
  73. 57 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineDeviceServiceImpl.java
  74. 125 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineLogServiceImpl.java
  75. 643 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineServiceImpl.java
  76. 455 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/subscribe/TriggerDeviceUtil.java
  77. 37 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/util/CronUtil.java
  78. 77 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/util/DateTimeUtil.java
  79. 26 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/util/JsonToStringDeserializer.java
  80. 81 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/util/JsonUtil.java
  81. 6 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/util/RuleEngineCallBack.java
  82. 14 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/util/RuleEngineUtil.java
  83. 245 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/Condition.java
  84. 122 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/ConditionExpression.java
  85. 277 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/DataPointVO.java
  86. 101 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/DeviceDataVO.java
  87. 107 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/Expression.java
  88. 136 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/Result.java
  89. 324 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineConditionVO.java
  90. 37 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineConfigDTO.java
  91. 395 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineContent.java
  92. 353 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineCronVO.java
  93. 316 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineDTO.java
  94. 202 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineDetail.java
  95. 324 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineDeviceVO.java
  96. 29 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineLogPageRequest.java
  97. 480 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineLogVO.java
  98. 19 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEnginePageRequest.java
  99. 411 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineVO.java
  100. 107 0
      service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/TimeRange.java

+ 2 - 1
pom.xml

@@ -64,7 +64,6 @@
 
         <module>service-agbox</module>
 
-
         <module>service-job</module>
 
 
@@ -88,6 +87,8 @@
 
         <module>service-tsdb</module>
 
+        <module>service-rule</module>
+
         <!--    <module>service-data</module>-->
 
     </modules>

+ 17 - 0
service-rule/pom.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>usky-modules</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-rule</artifactId>
+    <packaging>pom</packaging>
+    <modules>
+        <module>service-rule-biz</module>
+        <module>service-rule-api</module>
+    </modules>
+</project>

+ 26 - 0
service-rule/service-rule-api/pom.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>service-rule</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-rule-api</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>usky-common-core</artifactId>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+</project>

+ 84 - 0
service-rule/service-rule-biz/pom.xml

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>service-rule</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-rule-biz</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>common-cloud-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-rule-api</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>ruoyi-common-swagger</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.28</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-spring-boot-starter</artifactId>
+            <version>2.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-quartz</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.cache2k</groupId>
+            <artifactId>cache2k-core</artifactId>
+            <version>2.6.1.Final</version> <!-- 稳定版,兼容JDK8+ -->
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>13.0</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.2.6.RELEASE</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 44 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/RuoYiSystemApplication.java

@@ -0,0 +1,44 @@
+package com.usky.rule;
+
+import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
+import org.mybatis.spring.annotation.MapperScan;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.core.env.Environment;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * 数据规则引擎服务(对标 leo-rule-engine + MQTT 上报链路,RocketMQ 订阅触发)
+ */
+@EnableCustomSwagger2
+@EnableFeignClients(basePackages = "com.usky")
+@EnableScheduling
+@MapperScan("com.usky.rule.mapper")
+@ComponentScan("com.usky")
+@SpringBootApplication
+public class RuoYiSystemApplication {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(RuoYiSystemApplication.class);
+
+    public static void main(String[] args) throws UnknownHostException {
+        ConfigurableApplicationContext application = SpringApplication.run(RuoYiSystemApplication.class, args);
+        Environment env = application.getEnvironment();
+        String ip = InetAddress.getLocalHost().getHostAddress();
+        String port = env.getProperty("server.port");
+        String path = env.getProperty("server.servlet.context-path");
+        LOGGER.info("\n----------------------------------------------------------\n\t" +
+                "Application is running! Access URLs:\n\t" +
+                "Local: \t\thttp://localhost:" + port + (path == null ? "" : path) + "/\n\t" +
+                "External: \thttp://" + ip + ":" + port + (path == null ? "" : path) + "/\n\t" +
+                "Api: \t\thttp://" + ip + ":" + port + (path == null ? "" : path) + "/swagger-ui/index.html\n\t" +
+                "----------------------------------------------------------");
+    }
+}

+ 113 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/cache/DeviceTriggerIncludeMinuteCache.java

@@ -0,0 +1,113 @@
+package com.usky.rule.cache;
+
+import com.usky.common.redis.core.RedisHelper;
+import com.usky.rule.util.JsonUtil;
+import com.usky.rule.vo.ConditionExpression;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 设备触发“包含分钟”条件缓存,使用 RedisHelper 存储。
+ * 单条条件过期时间 1 天;按 ruleEngineId 可批量删除。
+ */
+@Component
+public class DeviceTriggerIncludeMinuteCache {
+
+    private static final String KEY_PREFIX = "ruleEngine:condition:";
+    private static final String INDEX_KEY_PREFIX = "ruleEngine:keys:";
+    private static final long EXPIRE_DAYS = 1L;
+
+    @Autowired
+    private RedisHelper redisHelper;
+
+    public ConditionExpression getConditions(Long ruleEngineId, String device, String identifier, String expression) {
+        String key = buildConditionKey(ruleEngineId, device, identifier, expression);
+        if (!redisHelper.hasKey(key)) {
+            return null;
+        }
+        Object val = redisHelper.get(key);
+        if (val == null) {
+            return null;
+        }
+        return JsonUtil.toObject(val.toString(), ConditionExpression.class);
+    }
+
+    public void setCondition(Long ruleEngineId, String device, String identifier, ConditionExpression condition) {
+        String key = buildConditionKey(ruleEngineId, device, identifier, condition.getExpression());
+        redisHelper.set(key, JsonUtil.toJson(condition), EXPIRE_DAYS, TimeUnit.DAYS);
+        addToIndex(ruleEngineId, key);
+    }
+
+    public void removeCondition(Long ruleEngineId, String device, String identifier, String expression) {
+        String key = buildConditionKey(ruleEngineId, device, identifier, expression);
+        redisHelper.delete(key);
+        removeFromIndex(ruleEngineId, key);
+    }
+
+    public void removeConditions(Long ruleEngineId, String device, String identifier, List<String> expressionList) {
+        if (expressionList == null || expressionList.isEmpty()) {
+            return;
+        }
+        for (String expression : expressionList) {
+            removeCondition(ruleEngineId, device, identifier, expression);
+        }
+    }
+
+    public void deleteConditions(Long ruleEngineId) {
+        String indexKey = INDEX_KEY_PREFIX + ruleEngineId;
+        if (!redisHelper.hasKey(indexKey)) {
+            return;
+        }
+        Object val = redisHelper.get(indexKey);
+        if (val != null) {
+            List<String> keys = JsonUtil.parseJsonArray(val.toString(), String.class);
+            if (keys != null) {
+                for (String key : keys) {
+                    redisHelper.delete(key);
+                }
+            }
+        }
+        redisHelper.delete(indexKey);
+    }
+
+    private static String buildConditionKey(Long ruleEngineId, String device, String identifier, String expression) {
+        return KEY_PREFIX + ruleEngineId + ":" + device + ":" + identifier + ":" + expression;
+    }
+
+    private void addToIndex(Long ruleEngineId, String conditionKey) {
+        String indexKey = INDEX_KEY_PREFIX + ruleEngineId;
+        List<String> keys = getIndexKeys(indexKey);
+        if (!keys.contains(conditionKey)) {
+            keys.add(conditionKey);
+            redisHelper.set(indexKey, JsonUtil.toJson(keys));
+        }
+    }
+
+    private void removeFromIndex(Long ruleEngineId, String conditionKey) {
+        String indexKey = INDEX_KEY_PREFIX + ruleEngineId;
+        List<String> keys = getIndexKeys(indexKey);
+        if (keys.remove(conditionKey)) {
+            if (keys.isEmpty()) {
+                redisHelper.delete(indexKey);
+            } else {
+                redisHelper.set(indexKey, JsonUtil.toJson(keys));
+            }
+        }
+    }
+
+    private List<String> getIndexKeys(String indexKey) {
+        if (!redisHelper.hasKey(indexKey)) {
+            return new ArrayList<>();
+        }
+        Object val = redisHelper.get(indexKey);
+        if (val == null) {
+            return new ArrayList<>();
+        }
+        List<String> list = JsonUtil.parseJsonArray(val.toString(), String.class);
+        return list != null ? list : new ArrayList<>();
+    }
+}

+ 32 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/cache/RuleEngineCache.java

@@ -0,0 +1,32 @@
+package com.usky.rule.cache;
+
+import com.usky.rule.vo.trigger.DeviceTrigger;
+import com.usky.rule.vo.trigger.SpaceTrigger;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.cache2k.Cache;
+import org.cache2k.Cache2kBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class RuleEngineCache {
+    public RuleEngineCache() {
+    }
+
+    @Bean(
+            name = {"consumptionTriggerCache"}
+    )
+    public Cache<Long, List<DeviceTrigger>> consumptionTriggerCache() {
+        return (new Cache2kBuilder<Long, List<DeviceTrigger>>() {
+        }).name("consumptionTriggerCache").eternal(false).expireAfterWrite(1L, TimeUnit.HOURS).entryCapacity(100000L).build();
+    }
+
+    @Bean(
+            name = {"spaceTriggerCache"}
+    )
+    public Cache<Long, List<SpaceTrigger>> spaceTriggerCache() {
+        return (new Cache2kBuilder<Long, List<SpaceTrigger>>() {
+        }).name("spaceTriggerCache").eternal(false).expireAfterWrite(1L, TimeUnit.HOURS).entryCapacity(100000L).build();
+    }
+}

+ 132 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/config/CronTaskManager.java

@@ -0,0 +1,132 @@
+package com.usky.rule.config;
+
+import com.usky.rule.util.RuleEngineUtil;
+import com.usky.rule.vo.action.RuleEngineAction;
+import com.usky.rule.vo.constraint.CronConstraint;
+import com.usky.rule.vo.constraint.DeviceConstraint;
+import com.usky.rule.jobs.ConsumptionJob;
+import com.usky.rule.listeners.CommonListener;
+import com.usky.rule.subscribe.TriggerDeviceUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.CronTrigger;
+import org.quartz.Job;
+import org.quartz.JobBuilder;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.TriggerBuilder;
+import org.quartz.TriggerKey;
+import org.quartz.impl.matchers.GroupMatcher;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CronTaskManager {
+    private Scheduler scheduler;
+    private TriggerDeviceUtil triggerDeviceUtil;
+
+    public void addJob(String jobName, String jobGroup, String cronExpression, Class<? extends Job> jobClass, List<CronConstraint> cronConstraintList, List<DeviceConstraint> deviceConstraints, List<RuleEngineAction> actions, Long ruleEngineId, Long projectId, Long spaceId) {
+        JobDataMap jobDataMap = new JobDataMap();
+        jobDataMap.put("actions", actions);
+        jobDataMap.put("ruleEngineId", ruleEngineId);
+        jobDataMap.put("projectId", projectId);
+        jobDataMap.put("spaceId", spaceId);
+        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).usingJobData(jobDataMap).build();
+        CronTrigger cronTrigger = (CronTrigger)TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).build();
+
+        try {
+            if (cronConstraintList != null || deviceConstraints != null) {
+                CommonListener jobListener = new CommonListener(this.getListenerName(ruleEngineId), this.triggerDeviceUtil, cronConstraintList, deviceConstraints);
+                this.scheduler.getListenerManager().addJobListener(jobListener, GroupMatcher.jobGroupEquals(jobGroup));
+            }
+
+            this.scheduler.scheduleJob(jobDetail, cronTrigger);
+        } catch (SchedulerException e) {
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    public String getListenerName(Long ruleEngineId) {
+        return "ruleEngineListener-" + ruleEngineId;
+    }
+
+    public void updateJob(String jobName, String jobGroup, String cronExpression) throws SchedulerException {
+        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
+        CronTrigger cronTrigger = (CronTrigger)this.scheduler.getTrigger(triggerKey);
+        if (cronTrigger == null) {
+            throw new SchedulerException("定时任务不存在");
+        } else {
+            String oldCronExpression = cronTrigger.getCronExpression();
+            if (!oldCronExpression.equalsIgnoreCase(cronExpression)) {
+                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
+                cronTrigger = (CronTrigger)cronTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
+                this.scheduler.rescheduleJob(triggerKey, cronTrigger);
+            }
+
+        }
+    }
+
+    public void deleteJob(String jobName, String jobGroup) throws SchedulerException {
+        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
+        if (this.scheduler.checkExists(jobKey)) {
+            this.scheduler.deleteJob(jobKey);
+        }
+
+    }
+
+    public boolean deleteAllJobsInJobGroup(Long ruleEngineId) {
+        String jobGroup = RuleEngineUtil.getJobGroup(ruleEngineId);
+
+        try {
+            List<JobKey> jobKeys = new ArrayList(this.scheduler.getJobKeys(GroupMatcher.jobGroupEquals(jobGroup)));
+            if (!jobKeys.isEmpty()) {
+                this.scheduler.deleteJobs(jobKeys);
+                this.scheduler.getListenerManager().removeJobListenerMatcher(this.getListenerName(ruleEngineId), GroupMatcher.jobGroupEquals(jobGroup));
+            }
+
+            return true;
+        } catch (SchedulerException var4) {
+            return false;
+        }
+    }
+
+    public void pauseAllJobs() throws SchedulerException {
+        this.scheduler.pauseAll();
+    }
+
+    public void resumeAllJobs() throws SchedulerException {
+        this.scheduler.resumeAll();
+    }
+
+    public void resumePausedJobs() throws SchedulerException {
+        for(String groupName : this.scheduler.getPausedTriggerGroups()) {
+            for(JobKey jobKey : this.scheduler.getJobKeys(GroupMatcher.groupEquals(groupName))) {
+                this.scheduler.resumeJob(jobKey);
+            }
+        }
+
+    }
+
+    public void performConsumptionTask() {
+        JobDetail consumptionJob = JobBuilder.newJob(ConsumptionJob.class).withIdentity("consumption", "device").build();
+        CronTrigger consumptionTrigger = (CronTrigger)TriggerBuilder.newTrigger().forJob(consumptionJob).withIdentity("consumptionTrigger").withSchedule(CronScheduleBuilder.cronSchedule("0 0 * * * ?")).build();
+//        JobDetail spaceJob = JobBuilder.newJob(SpaceJob.class).withIdentity("space").build();
+//        CronTrigger spaceTrigger = (CronTrigger)TriggerBuilder.newTrigger().forJob(spaceJob).withIdentity("spaceTrigger", "space").withSchedule(CronScheduleBuilder.cronSchedule("0 0 * * * ?")).build();
+
+        try {
+            this.scheduler.scheduleJob(consumptionJob, consumptionTrigger);
+//            this.scheduler.scheduleJob(spaceJob, spaceTrigger);
+        } catch (SchedulerException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public CronTaskManager(final Scheduler scheduler, final TriggerDeviceUtil triggerDeviceUtil) {
+        this.scheduler = scheduler;
+        this.triggerDeviceUtil = triggerDeviceUtil;
+    }
+}

+ 22 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/constant/DateTimeConstants.java

@@ -0,0 +1,22 @@
+package com.usky.rule.constant;
+
+import com.usky.rule.util.DateTimeUtil;
+import java.time.format.DateTimeFormatter;
+
+public interface DateTimeConstants {
+    String PATTERN_NUMERIC_DATETIME = "yyyyMMddHHmmss";
+    String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
+    String PATTERN_DATE = "yyyy-MM-dd";
+    String PATTERN_TIME = "HH:mm:ss";
+    DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(DateTimeUtil.getZoneId());
+    DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(DateTimeUtil.getZoneId());
+    DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(DateTimeUtil.getZoneId());
+    DateTimeFormatter DATE_TIME_HOUR_FORMATTER_ZH_CN = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时").withZone(DateTimeUtil.getZoneId());
+    DateTimeFormatter DATE_TIME_DAY_FORMATTER_ZH_CN = DateTimeFormatter.ofPattern("yyyy年MM月dd日").withZone(DateTimeUtil.getZoneId());
+    DateTimeFormatter DATE_TIME_MONTH_FORMATTER_ZH_CN = DateTimeFormatter.ofPattern("yyyy年MM月").withZone(DateTimeUtil.getZoneId());
+    DateTimeFormatter DATE_TIME_YEAR_FORMATTER_ZH_CN = DateTimeFormatter.ofPattern("yyyy年").withZone(DateTimeUtil.getZoneId());
+    DateTimeFormatter SIMPLE_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(DateTimeUtil.getZoneId());
+    DateTimeFormatter SIMPLE_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd").withZone(DateTimeUtil.getZoneId());
+    DateTimeFormatter SIMPLE_TIME_FORMATTER = DateTimeFormatter.ofPattern("HHmmss").withZone(DateTimeUtil.getZoneId());
+    DateTimeFormatter DB_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(DateTimeUtil.getZoneId());
+}

+ 36 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/constant/RegExpConstants.java

@@ -0,0 +1,36 @@
+package com.usky.rule.constant;
+
+import java.util.regex.Pattern;
+
+public interface RegExpConstants {
+    String DATE_TIME = "^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)\\s+([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$";
+    Pattern DATE_TIME_PATTERN = Pattern.compile("^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)\\s+([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$");
+    String DATE = "^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$";
+    Pattern DATE_PATTERN = Pattern.compile("^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$");
+    String TIME = "^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$";
+    Pattern TIME_PATTERN = Pattern.compile("^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$");
+    String SIMPLE_DATE_TIME = "^(?:(?!0000)[0-9]{4}(?:(?:0[1-9]|1[0-2])(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])(?:29|30)|(?:0[13578]|1[02])31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)0229)([01][0-9]|2[0-3])[0-5][0-9][0-5][0-9]$";
+    Pattern SIMPLE_DATE_TIME_PATTERN = Pattern.compile("^(?:(?!0000)[0-9]{4}(?:(?:0[1-9]|1[0-2])(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])(?:29|30)|(?:0[13578]|1[02])31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)0229)([01][0-9]|2[0-3])[0-5][0-9][0-5][0-9]$");
+    String SIMPLE_DATE = "^(?:(?!0000)[0-9]{4}(?:(?:0[1-9]|1[0-2])(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])(?:29|30)|(?:0[13578]|1[02])31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)0229)$";
+    Pattern SIMPLE_DATE_PATTERN = Pattern.compile("^(?:(?!0000)[0-9]{4}(?:(?:0[1-9]|1[0-2])(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])(?:29|30)|(?:0[13578]|1[02])31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)0229)$");
+    String SIMPLE_TIME = "^([01][0-9]|2[0-3])[0-5][0-9][0-5][0-9]$";
+    Pattern SIMPLE_TIME_PATTERN = Pattern.compile("^([01][0-9]|2[0-3])[0-5][0-9][0-5][0-9]$");
+    String IP = "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$";
+    Pattern IP_PATTERN = Pattern.compile("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$");
+    String DEVICE_ID = "^[0-9a-zA-Z]{13}$";
+    Pattern DEVICE_ID_PATTERN = Pattern.compile("^[0-9a-zA-Z]{13}$");
+    String GATEWAY_ID = "^[0-9a-zA-Z]{13}$";
+    Pattern GATEWAY_ID_PATTERN = Pattern.compile("^[0-9a-zA-Z]{13}$");
+    String PRODUCT_IDENTIFIER = "^[0-9A-Z]{7}$";
+    Pattern PRODUCT_IDENTIFIER_PATTERN = Pattern.compile("^[0-9A-Z]{7}$");
+    String GATEWAY_SECRET = "^(?![0-9]+$)[0-9A-Za-z]{16}$";
+    Pattern GATEWAY_SECRET_PATTERN = Pattern.compile("^(?![0-9]+$)[0-9A-Za-z]{16}$");
+    String PRODUCT_TEMPLATE_IDENTIFIER = "[0-9a-zA-Z]{18}$";
+    String USERNAME = "^(?![0-9]+$)[0-9A-Za-z]{4,20}$";
+    String isNumeric = "-?\\d+(\\.\\d+)?";
+    String isInteger = "-?\\d+";
+    String PASSWORD = "^(?:(?=.*\\d)(?=.*[a-zA-Z])|(?=.*\\d)(?=.*[^a-zA-Z\\d])|(?=.*[a-zA-Z])(?=.*[^a-zA-Z\\d]))[a-zA-Z\\d\\S]{8,18}$";
+    String PHONE = "^[1]\\d{10}$";
+    String HOUR = "^(2[0-3]|[0-1]?\\d)$";
+    String MONTH = "^(2[0-8]|[0-1]?[1-9])$";
+}

+ 108 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/MybatisGeneratorUtils.java

@@ -0,0 +1,108 @@
+package com.usky.rule.controller;//package com.usky.iot.controller;//package com.usky.dm.controller.web.business;//package com.usky.dm.controller.web;
+
+
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.generator.AutoGenerator;
+import com.baomidou.mybatisplus.generator.InjectionConfig;
+import com.baomidou.mybatisplus.generator.config.*;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author yq
+ * @date 2021/7/6 11:42
+ */
+public class MybatisGeneratorUtils {
+    public static void main(String[] args) {
+
+            shell("service-rule","service-rule-biz");
+    }
+
+    private static void shell(String parentName,String model) {
+
+        AutoGenerator mpg = new AutoGenerator();
+        //1、全局配置
+        GlobalConfig gc = new GlobalConfig();
+//        File file = new File(model);
+//        String path = file.getAbsolutePath();
+        String projectPath = System.getProperty("user.dir");
+        projectPath+="/"+parentName;
+        projectPath+="/"+model;
+        gc.setOutputDir(projectPath+ "/src/main/java");  //生成路径(一般都是生成在此项目的src/main/java下面)
+        //修改为自己的名字
+        gc.setAuthor("zyj"); //设置作者
+        gc.setOpen(false);
+        gc.setFileOverride(true); //第二次生成会把第一次生成的覆盖掉
+        gc.setServiceName("%sService"); //生成的service接口名字首字母是否为I,这样设置就没有
+        gc.setBaseResultMap(true); //生成resultMap
+        mpg.setGlobalConfig(gc);
+
+        //2、数据源配置
+        //修改数据源
+        DataSourceConfig dsc = new DataSourceConfig();
+        dsc.setUrl("jdbc:mysql://192.168.10.165:3306/usky-cloud?useUnicode=true&serverTimezone=GMT&useSSL=false&characterEncoding=utf8");
+        dsc.setDriverName("com.mysql.jdbc.Driver");
+        dsc.setUsername("root");
+        dsc.setPassword("yt123456");
+        mpg.setDataSource(dsc);
+
+        // 3、包配置
+        PackageConfig pc = new PackageConfig();
+        pc.setParent("com.usky.rule");
+        pc.setController("controller.web");
+        pc.setEntity("domain");
+        pc.setMapper("mapper");
+        pc.setService("service");
+        pc.setServiceImpl("service.impl");
+//        pc.setXml("mapper.demo");
+        //pc.setModuleName("test");
+        mpg.setPackageInfo(pc);
+
+        // 4、策略配置
+        StrategyConfig strategy = new StrategyConfig();
+        strategy.setNaming(NamingStrategy.underline_to_camel);
+        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
+        strategy.setSuperMapperClass("com.usky.common.mybatis.core.CrudMapper");
+        strategy.setSuperServiceClass("com.usky.common.mybatis.core.CrudService");
+        strategy.setSuperServiceImplClass("com.usky.common.mybatis.core.AbstractCrudService");
+        // strategy.setTablePrefix("t_"); // 表名前缀
+        strategy.setEntityLombokModel(true); //使用lombok
+        //修改自己想要生成的表
+        strategy.setInclude("base_space");  // 逆向工程使用的表   如果要生成多个,这里可以传入String[]
+        mpg.setStrategy(strategy);
+
+        // 关闭默认 xml 生成,调整生成 至 根目录
+        //修改对应的模块名称
+        TemplateConfig tc = new TemplateConfig();
+        // 自定义配置
+        InjectionConfig cfg = new InjectionConfig() {
+            @Override
+            public void initMap() {
+                // to do nothing
+            }
+        };
+        //如果模板引擎是 velocity
+        String templatePath = "/templates/mapper.xml.vm";
+        // 自定义输出配置
+        List<FileOutConfig> focList = new ArrayList<>();
+        // 自定义配置会被优先输出
+        String finalProjectPath = projectPath;
+        focList.add(new FileOutConfig(templatePath) {
+            @Override
+            public String outputFile(TableInfo tableInfo) {
+                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
+                return finalProjectPath + "/src/main/resources/mapper/rule" + "/"
+                        + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
+            }
+        });
+        cfg.setFileOutConfigList(focList);
+        mpg.setCfg(cfg);
+        tc.setXml(null);
+        mpg.setTemplate(tc);
+        //5、执行
+        mpg.execute();
+    }
+}

+ 21 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/BaseBuildController.java

@@ -0,0 +1,21 @@
+package com.usky.rule.controller.web;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.stereotype.Controller;
+
+/**
+ * <p>
+ * 建筑信息 前端控制器
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Controller
+@RequestMapping("/baseBuild")
+public class BaseBuildController {
+
+}
+

+ 21 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/BaseSpaceController.java

@@ -0,0 +1,21 @@
+package com.usky.rule.controller.web;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.stereotype.Controller;
+
+/**
+ * <p>
+ * 空间 前端控制器
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Controller
+@RequestMapping("/baseSpace")
+public class BaseSpaceController {
+
+}
+

+ 21 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/DmpDeviceController.java

@@ -0,0 +1,21 @@
+package com.usky.rule.controller.web;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.stereotype.Controller;
+
+/**
+ * <p>
+ * 设备信息表 前端控制器
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Controller
+@RequestMapping("/dmpDevice")
+public class DmpDeviceController {
+
+}
+

+ 21 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/DmpProductAttributeController.java

@@ -0,0 +1,21 @@
+package com.usky.rule.controller.web;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.stereotype.Controller;
+
+/**
+ * <p>
+ * 产品属性表 前端控制器
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Controller
+@RequestMapping("/dmpProductAttribute")
+public class DmpProductAttributeController {
+
+}
+

+ 21 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/DmpProductController.java

@@ -0,0 +1,21 @@
+package com.usky.rule.controller.web;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.stereotype.Controller;
+
+/**
+ * <p>
+ * 产品信息表 前端控制器
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Controller
+@RequestMapping("/dmpProduct")
+public class DmpProductController {
+
+}
+

+ 116 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/RuleEngineController.java

@@ -0,0 +1,116 @@
+package com.usky.rule.controller.web;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.common.log.annotation.Log;
+import com.usky.common.log.enums.BusinessType;
+import com.usky.common.security.utils.SecurityUtils;
+import com.usky.rule.domain.RuleEngine;
+import com.usky.rule.domain.RuleEngineCondition;
+import com.usky.rule.service.RuleEngineConditionService;
+import com.usky.rule.service.RuleEngineService;
+import com.usky.rule.vo.RuleEngineConfigDTO;
+import com.usky.rule.vo.RuleEngineDTO;
+import com.usky.rule.vo.RuleEnginePageRequest;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 数据规则引擎界面 API(表 rule_engine)
+ * 含:配置规则、条件下拉、触发
+ */
+@Api(tags = "数据规则引擎")
+@RestController
+@RequestMapping("/ruleEngine")
+public class RuleEngineController {
+    private final RuleEngineService ruleEngineService;
+    private final RuleEngineConditionService ruleEngineConditionService;
+
+    public RuleEngineController(RuleEngineService ruleEngineService,
+                               RuleEngineConditionService ruleEngineConditionService) {
+        this.ruleEngineService = ruleEngineService;
+        this.ruleEngineConditionService = ruleEngineConditionService;
+    }
+
+    @ApiOperation("新增规则引擎")
+    @PostMapping
+    public ApiResult<Void> add(@RequestBody RuleEngine ruleEngine) {
+        ruleEngineService.add(ruleEngine);
+        return ApiResult.success();
+    }
+
+    @ApiOperation("修改规则引擎")
+    @PutMapping
+    public ApiResult<Void> edit(@RequestBody RuleEngine ruleEngine) {
+        ruleEngineService.update(ruleEngine);
+        return ApiResult.success();
+    }
+
+    @ApiOperation("删除规则引擎")
+    @DeleteMapping("/{id}")
+    public ApiResult<Void> remove(@PathVariable("id") Long id) {
+        ruleEngineService.remove(id);
+        return ApiResult.success();
+    }
+
+    @ApiOperation("规则引擎详情")
+    @GetMapping("/{id}")
+    public ApiResult<RuleEngine> detail(@PathVariable("id") Long id) {
+        return ApiResult.success(ruleEngineService.getById(id));
+    }
+
+    @ApiOperation("规则引擎分页")
+    @PostMapping("/page")
+    public ApiResult<CommonPage<RuleEngine>> page(@RequestBody RuleEnginePageRequest request) {
+        return ApiResult.success(ruleEngineService.pageList(request));
+    }
+
+    @ApiOperation("查询多条数据(条件同分页,不分页,对应 leo list 接口)")
+    @PostMapping("/list")
+    public ApiResult<List<RuleEngine>> list(@RequestBody RuleEnginePageRequest request) {
+        return ApiResult.success(ruleEngineService.list(request));
+    }
+
+    @ApiOperation("规则启停")
+    @PutMapping("/{id}/status")
+    public ApiResult<Void> status(@PathVariable("id") Long id, @RequestParam("status") Integer status) {
+        ruleEngineService.updateStatus(id, status);
+        return ApiResult.success();
+    }
+
+
+    // --------------- 配置规则、条件下拉、触发 ---------------
+
+    @ApiOperation("配置规则")
+    @PostMapping("/ruleEngineConfig")
+    public ApiResult<Void> ruleEngineConfig(@RequestBody RuleEngineDTO dto) {
+        ruleEngineService.ruleEngineConfig(dto);
+        return ApiResult.success();
+    }
+
+    @ApiOperation("条件下拉")
+    @GetMapping("/conditions")
+    public ApiResult<List<RuleEngineCondition>> conditions(
+            @ApiParam(value = "类型:1 触发条件,2 约束条件,不传则返回全部") @RequestParam(required = false) Integer type) {
+        List<RuleEngineCondition> list = ruleEngineConditionService.listConditions(type);
+        return ApiResult.success(list);
+    }
+
+    @ApiOperation("手动触发规则")
+    @PostMapping("/{id}/manualTrigger")
+    public ApiResult<Void> manualTrigger(@PathVariable("id") Long id) {
+        ruleEngineService.manualTrigger(id);
+        return ApiResult.success();
+    }
+
+    @ApiOperation("获取规则完整配置")
+    @GetMapping("/{id}/getEngineConfig")
+    public ApiResult<RuleEngineConfigDTO> getEngineConfig(@PathVariable("id") Long id) {
+        RuleEngineConfigDTO config = ruleEngineService.getEngineConfig(id);
+        return ApiResult.success(config);
+    }
+}

+ 53 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/controller/web/RuleEngineLogController.java

@@ -0,0 +1,53 @@
+package com.usky.rule.controller.web;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.common.log.annotation.Log;
+import com.usky.common.log.enums.BusinessType;
+import com.usky.common.security.utils.SecurityUtils;
+import com.usky.rule.domain.RuleEngineLog;
+import com.usky.rule.service.RuleEngineLogService;
+import com.usky.rule.vo.RuleEngineContent;
+import com.usky.rule.vo.RuleEngineLogPageRequest;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 数据规则日志界面 API(表 rule_engine_log)
+ */
+@Api(tags = "数据规则日志")
+@RestController
+@RequestMapping("/ruleEngineLog")
+public class RuleEngineLogController {
+    private final RuleEngineLogService ruleEngineLogService;
+
+    public RuleEngineLogController(RuleEngineLogService ruleEngineLogService) {
+        this.ruleEngineLogService = ruleEngineLogService;
+    }
+
+    @ApiOperation("规则执行日志分页")
+    @PostMapping("/page")
+    public ApiResult<CommonPage<RuleEngineLog>> page(@RequestBody RuleEngineLogPageRequest request) {
+        if (request.getTenantId() == null) {
+            try {
+                request.setTenantId(SecurityUtils.getTenantId());
+            } catch (Exception ignored) {
+            }
+        }
+        return ApiResult.success(ruleEngineLogService.page(request));
+    }
+
+    @ApiOperation("规则执行日志详情")
+    @GetMapping("/{id}")
+    public ApiResult<RuleEngineContent> detail(@PathVariable("id") Long id) {
+        return ApiResult.success(ruleEngineLogService.getById(id));
+    }
+
+    @ApiOperation("删除规则执行日志")
+    @DeleteMapping("/{id}")
+    public ApiResult<Void> remove(@PathVariable("id") Long id) {
+        ruleEngineLogService.remove(id);
+        return ApiResult.success();
+    }
+}

+ 155 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/crons/TriggerCronTask.java

@@ -0,0 +1,155 @@
+package com.usky.rule.crons;
+
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.usky.rule.domain.RuleEngine;
+import com.usky.rule.service.RuleEngineCronService;
+import com.usky.rule.service.RuleEngineService;
+import com.usky.rule.util.CronUtil;
+import com.usky.rule.util.JsonUtil;
+import com.usky.rule.util.RuleEngineUtil;
+import com.usky.rule.vo.RuleEngineCronVO;
+import com.usky.rule.vo.RuleEngineDetail;
+import com.usky.rule.vo.action.RuleEngineAction;
+import com.usky.rule.vo.constraint.CronConstraint;
+import com.usky.rule.vo.constraint.DeviceConstraint;
+import com.usky.rule.vo.trigger.CronTrigger;
+import com.usky.rule.vo.trigger.DeviceTrigger;
+import com.usky.rule.vo.RuleEnginePageRequest;
+import com.usky.rule.vo.trigger.SpaceTrigger;
+import com.usky.rule.config.CronTaskManager;
+import com.usky.rule.jobs.CommonJob;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.cache2k.Cache;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.SmartLifecycle;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TriggerCronTask implements ApplicationContextAware, InitializingBean, DisposableBean, SmartLifecycle {
+    private RuleEngineService ruleEngineService;
+    private RuleEngineCronService ruleEngineCronService;
+    private CronTaskManager cronTaskManager;
+    private Cache<Long, List<DeviceTrigger>> consumptionTriggerCache;
+    private Cache<Long, List<SpaceTrigger>> spaceTriggerCache;
+
+    public void start() {
+        List<RuleEngine> enabledRuleEngineList = this.getAllEnabledRuleEngine();
+        enabledRuleEngineList = (List)enabledRuleEngineList.stream().filter((ruleEnginex) -> StringUtils.isNotBlank(ruleEnginex.getDetail())).collect(Collectors.toList());
+        if (!enabledRuleEngineList.isEmpty()) {
+            for(RuleEngine ruleEngine : enabledRuleEngineList) {
+                RuleEngineDetail ruleEngineDetail = (RuleEngineDetail)JsonUtil.toObject(ruleEngine.getDetail(), RuleEngineDetail.class);
+                List<CronTrigger> cronTriggers = this.ruleEngineService.getCronTriggers(ruleEngineDetail.getTriggers());
+                if (cronTriggers != null) {
+                    cronTriggers = (List)cronTriggers.stream().filter((cronTrigger) -> CronUtil.isCronMatched(cronTrigger.getCron())).collect(Collectors.toList());
+                    if (!cronTriggers.isEmpty()) {
+                        this.processCronTask(ruleEngine.getId(), ruleEngine.getProjectId(), ruleEngine.getSpaceId(), ruleEngineDetail, cronTriggers);
+                    }
+                }
+
+                this.setDeviceConsumptionCache(ruleEngine.getId(), ruleEngineDetail);
+            }
+
+            this.cronTaskManager.performConsumptionTask();
+        }
+    }
+
+    private void startCronTask() {
+        List<RuleEngineCronVO> ruleEngineCronList = this.ruleEngineCronService.getTurnedOnCron();
+        Function<RuleEngineCronVO, String> ruleEngineCronFunction = (vo) -> vo.getRuleEngineId() + ":" + vo.getProjectId() + ":" + vo.getSpaceId();
+        Map<String, List<CronTrigger>> ruleEngineCronMap = (Map)ruleEngineCronList.stream().filter((cronTrigger) -> CronUtil.isCronMatched(cronTrigger.getCron())).collect(Collectors.groupingBy(ruleEngineCronFunction, Collectors.mapping((vo) -> new CronTrigger(vo.getCron()), Collectors.toList())));
+        if (CollectionUtils.isNotEmpty(ruleEngineCronMap)) {
+            ruleEngineCronMap.forEach((identifier, list) -> {
+                String[] idAndProjectId = identifier.split(":");
+                Long ruleEngineId = Long.valueOf(idAndProjectId[0]);
+                Long projectId = Long.valueOf(idAndProjectId[1]);
+                Long spaceId = Long.valueOf(idAndProjectId[2]);
+                String detail = this.ruleEngineService.getDetail(Long.valueOf(idAndProjectId[0]));
+                if (StringUtils.isNotEmpty(detail)) {
+                    RuleEngineDetail ruleEngineDetail = (RuleEngineDetail)JsonUtil.toObject(detail, RuleEngineDetail.class);
+                    this.processCronTask(ruleEngineId, projectId, spaceId, ruleEngineDetail, list);
+                }
+
+            });
+        }
+
+    }
+
+    public void setDeviceConsumptionCache(Long ruleEngineId, RuleEngineDetail ruleEngineDetail) {
+        List<DeviceTrigger> deviceTriggers = this.ruleEngineService.getDeviceTriggers(ruleEngineDetail.getTriggers());
+        if (deviceTriggers != null && !deviceTriggers.isEmpty()) {
+            List<DeviceTrigger> consumptionTrigger = (List)deviceTriggers.stream().filter((deviceTrigger) -> deviceTrigger.getMethod().equals("consumption")).collect(Collectors.toList());
+            if (!consumptionTrigger.isEmpty()) {
+                this.consumptionTriggerCache.put(ruleEngineId, consumptionTrigger);
+            }
+        }
+
+        List<SpaceTrigger> spaceTriggers = this.ruleEngineService.getSpaceTriggers(ruleEngineDetail.getTriggers());
+        if (deviceTriggers != null && !spaceTriggers.isEmpty()) {
+            this.spaceTriggerCache.put(ruleEngineId, spaceTriggers);
+        }
+
+    }
+
+    private void processCronTask(Long id, Long projectId, Long spaceId, RuleEngineDetail engineDetail, List<CronTrigger> cronTriggers) {
+        List<CronConstraint> cronConstraints = this.ruleEngineService.getCronConstraints(engineDetail.getConstraints());
+        List<DeviceConstraint> deviceConstraints = this.ruleEngineService.getDeviceConstraints(engineDetail.getConstraints());
+        List<RuleEngineAction> actions = this.ruleEngineService.getActions(engineDetail.getActions());
+        if (actions != null && !actions.isEmpty()) {
+            addCronJob(id, projectId, spaceId, cronTriggers, cronConstraints, deviceConstraints, actions, this.cronTaskManager);
+        }
+
+    }
+
+    public static void addCronJob(Long ruleEngineId, Long projectId, Long spaceId, List<CronTrigger> cronTriggers, List<CronConstraint> cronConstraints, List<DeviceConstraint> deviceConstraints, List<RuleEngineAction> actions, CronTaskManager cronTaskManager) {
+        String jobGroup = RuleEngineUtil.getJobGroup(ruleEngineId);
+
+        for(int i = 0; i < cronTriggers.size(); ++i) {
+            CronTrigger cronTrigger = (CronTrigger)cronTriggers.get(i);
+            String jobName = RuleEngineUtil.getTriggerCronJobName(i);
+            cronTaskManager.addJob(jobName, jobGroup, cronTrigger.getCron(), CommonJob.class, cronConstraints, deviceConstraints, actions, ruleEngineId, projectId, spaceId);
+        }
+
+    }
+
+    /** 查询所有已启用规则(status=1),使用 list(RuleEnginePageRequest) 替代 MyBatis-Plus list(Wrapper) */
+    public List<RuleEngine> getAllEnabledRuleEngine() {
+        RuleEnginePageRequest request = new RuleEnginePageRequest();
+        request.setStatus(1);
+        return this.ruleEngineService.list(request);
+    }
+
+    public void destroy() throws Exception {
+    }
+
+    public void afterPropertiesSet() throws Exception {
+    }
+
+    public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
+    }
+
+    public void stop() {
+    }
+
+    public boolean isRunning() {
+        return false;
+    }
+
+    public TriggerCronTask(final RuleEngineService ruleEngineService, final RuleEngineCronService ruleEngineCronService, final CronTaskManager cronTaskManager, final Cache<Long, List<DeviceTrigger>> consumptionTriggerCache, final Cache<Long, List<SpaceTrigger>> spaceTriggerCache) {
+        this.ruleEngineService = ruleEngineService;
+        this.ruleEngineCronService = ruleEngineCronService;
+        this.cronTaskManager = cronTaskManager;
+        this.consumptionTriggerCache = consumptionTriggerCache;
+        this.spaceTriggerCache = spaceTriggerCache;
+    }
+}

+ 212 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/BaseBuild.java

@@ -0,0 +1,212 @@
+package com.usky.rule.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import java.time.LocalDate;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 建筑信息
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class BaseBuild implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 建筑编号
+     */
+    private String buildNum;
+
+    /**
+     * 建筑名称
+     */
+    private String buildName;
+
+    /**
+     * 详细地址
+     */
+    private String address;
+
+    /**
+     * 模型地址
+     */
+    private String modelAddress;
+
+    /**
+     * 地上楼层
+     */
+    private Integer aboveFloor;
+
+    /**
+     * 地下楼层
+     */
+    private Integer underFloor;
+
+    /**
+     * 建筑面积
+     */
+    private Double buildArea;
+
+    /**
+     * 占地面积
+     */
+    private Double coverArea;
+
+    /**
+     * 耐火等级
+     */
+    private Integer fireRating;
+
+    /**
+     * 使用性质
+     */
+    private Integer useCharacter;
+
+    /**
+     * 建筑结构
+     */
+    private Integer buildStructure;
+
+    /**
+     * 建筑高度
+     */
+    private Double buildHigh;
+
+    /**
+     * 建筑高度分类
+     */
+    private Integer highType;
+
+    /**
+     * 竣工年份
+     */
+    private LocalDate completeYear;
+
+    /**
+     * 安全责任人
+     */
+    private String safePerson;
+
+    /**
+     * 安全管理人
+     */
+    private String managePerson;
+
+    /**
+     * 火灾危险性
+     */
+    private Integer fireRisk;
+
+    /**
+     * 消防控制室位置
+     */
+    private String fireControlRoom;
+
+    /**
+     * 建筑立面图
+     */
+    private String buildInside;
+
+    /**
+     * 建筑平面图
+     */
+    private String buildPlan;
+
+    /**
+     * 设施ID
+     */
+    private Integer facilityId;
+
+    /**
+     * BIM地址
+     */
+    private String bimUrl;
+
+    /**
+     * 联系人电话
+     */
+    private String contactPhone;
+
+    /**
+     * 建筑备注
+     */
+    private String buildDesc;
+
+    /**
+     * 单元数量
+     */
+    private Integer unitCount;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+
+    /**
+     * 更新人
+     */
+    private String updateBy;
+
+    /**
+     * 创建人
+     */
+    private String createBy;
+
+    /**
+     * 删除标识
+     */
+    private Integer deleteFlag;
+
+    /**
+     * 地下空间
+     */
+    private Double underSpace;
+
+    /**
+     * 防火涂层(0、无 1、有)
+     */
+    private Integer fireproofCoat;
+
+    /**
+     * 组织机构ID
+     */
+    private Integer deptId;
+
+    /**
+     * 租户ID
+     */
+    private Integer tenantId;
+
+    /**
+     * 经度(当设施类型为点时使用该字段)
+     */
+    private String longitude;
+
+    /**
+     * 纬度(当设施类型为点时使用该字段)
+     */
+    private String latitude;
+
+
+}

+ 91 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/BaseSpace.java

@@ -0,0 +1,91 @@
+package com.usky.rule.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 空间
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class BaseSpace implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 自增ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 名称
+     */
+    private String name;
+
+    /**
+     * 父节点ID
+     */
+    private Long parentId;
+
+    /**
+     * 空间类型 1:项目 2:区域 3:建筑 4:楼层 5:房间
+     */
+    private Integer type;
+
+    /**
+     * 根节点ID
+     */
+    private Long rootId;
+
+    /**
+     * 节点路径
+     */
+    private String path;
+
+    /**
+     * 节点路径名称
+     */
+    private String pathName;
+
+    /**
+     * 深度
+     */
+    private Integer deep;
+
+    /**
+     * 更新人
+     */
+    private Long updatedBy;
+
+    /**
+     * 记录更新时间
+     */
+    private LocalDateTime updateTime;
+
+    /**
+     * 创建人
+     */
+    private Long createdBy;
+
+    /**
+     * 记录创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+
+
+}

+ 156 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/DmpDevice.java

@@ -0,0 +1,156 @@
+package com.usky.rule.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 设备信息表
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DmpDevice implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 设备ID;设备注册时系统自动生成一个唯一编号
+     */
+    private String deviceId;
+
+    /**
+     * 设备名称
+     */
+    private String deviceName;
+
+    /**
+     * 设备类型(501、监控系统  502、门禁系统  503、梯控系统  504、机房系统  509、环境系统  510、照明系统)
+     */
+    private Integer deviceType;
+
+    /**
+     * 产品ID
+     */
+    private Integer productId;
+
+    /**
+     * 物联网卡号
+     */
+    private String simCode;
+
+    /**
+     * 国际移动用户识别码
+     */
+    private String imsiCode;
+
+    /**
+     * 自动订阅标识(0:否,1:是)
+     */
+    private Integer subscribeFlag;
+
+    /**
+     * 节点类型
+     */
+    private Integer nodeType;
+
+    /**
+     * 分组id
+     */
+    private Integer groupId;
+
+    /**
+     * 删除标识
+     */
+    private Integer deleteFlag;
+
+    /**
+     * 创建人
+     */
+    private String createdBy;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdTime;
+
+    /**
+     * 更新人
+     */
+    private String updatedBy;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updatedTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+
+    /**
+     * 单位编号
+     */
+    private String companyCode;
+
+    /**
+     * 安装位置
+     */
+    private String installAddress;
+
+    /**
+     * 业务状态;1:未激活,2:已激活,3:禁用
+     */
+    private Integer serviceStatus;
+
+    /**
+     * 产品编码
+     */
+    private String productCode;
+
+    /**
+     * 设备uuid
+     */
+    private String deviceUuid;
+
+    /**
+     * 经度
+     */
+    private String longitude;
+
+    /**
+     * 纬度
+     */
+    private String latitude;
+
+    /**
+     * 设备所属类型(1、普通设备  2、网关设备  3、网关子设备)
+     */
+    private Integer categoryType;
+
+    /**
+     * 所属网关
+     */
+    private String gatewayUuid;
+
+    /**
+     * 建筑ID
+     */
+    private Integer buildId;
+
+
+}

+ 166 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/DmpProduct.java

@@ -0,0 +1,166 @@
+package com.usky.rule.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 产品信息表
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DmpProduct implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 产品名称
+     */
+    private String productName;
+
+    /**
+     * 接入方式(1、设备直连  2、网关接入)
+     */
+    private Integer accessMode;
+
+    /**
+     * 网络类型(1、WIFI  2、移动蜂窝数据 3、NB-IoT 4、以太网)
+     */
+    private Integer networkType;
+
+    /**
+     * 设备类型(501、监控系统  502、门禁系统  503、梯控系统  504、机房系统  509、环境系统  510、照明系统)
+     */
+    private Integer deviceType;
+
+    /**
+     * 通信协议(1、MQTT  2、TCP设备直连 3、HTTP)
+     */
+    private Integer comProtocol;
+
+    /**
+     * 认证方式
+     */
+    private String authMode;
+
+    /**
+     * 设备型号
+     */
+    private String deviceModel;
+
+    /**
+     * 产品描述
+     */
+    private String productDescribe;
+
+    /**
+     * 厂家名称
+     */
+    private String factoryName;
+
+    /**
+     * 厂家联系人
+     */
+    private String factoryPerson;
+
+    /**
+     * 厂家联系电话
+     */
+    private String factoryPhone;
+
+    /**
+     * 资质证书1
+     */
+    private String certificateUrl1;
+
+    /**
+     * 资质证书2
+     */
+    private String certificateUrl2;
+
+    /**
+     * 资质证书3
+     */
+    private String certificateUrl3;
+
+    /**
+     * 协议文档
+     */
+    private String agreementUrl;
+
+    /**
+     * 删除标识
+     */
+    private Integer deleteFlag;
+
+    /**
+     * 创建人
+     */
+    private String createdBy;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdTime;
+
+    /**
+     * 更新人
+     */
+    private String updatedBy;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updatedTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+
+    /**
+     * 产品编码
+     */
+    private String productCode;
+
+    /**
+     * 项目ID
+     */
+    private Long projectId;
+
+    /**
+     * 产品模板ID
+     */
+    private Long productTemplateId;
+
+    /**
+     * 产品模板编码
+     */
+    private String productTemplateCode;
+
+    /**
+     * 产品分类ID
+     */
+    private Long productCategoryId;
+
+    /**
+     * 产品厂商ID
+     */
+    private Long productManufacturerId;
+
+
+}

+ 142 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/DmpProductAttribute.java

@@ -0,0 +1,142 @@
+package com.usky.rule.domain;
+
+import java.math.BigDecimal;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 产品属性表
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DmpProductAttribute implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 产品ID
+     */
+    private Integer productId;
+
+    /**
+     * 属性名称
+     */
+    private String attributeName;
+
+    /**
+     * 属性标识
+     */
+    private String attributeCode;
+
+    /**
+     * 对应端口/属性ID
+     */
+    private Integer attributePort;
+
+    /**
+     * 属性类型;1:必选,2:可选
+     */
+    private Integer attributeType;
+
+    /**
+     * 数据类型(1、数值型 2、字符型 3、bool型)
+     */
+    private Integer dataType;
+
+    /**
+     * 绑定状态;1:已绑定,2:未绑定
+     */
+    private Integer bindStatus;
+
+    /**
+     * 长度
+     */
+    private Integer attributeLength;
+
+    /**
+     * 单位
+     */
+    private String attributeUnit;
+
+    /**
+     * 最大值
+     */
+    private BigDecimal maximum;
+
+    /**
+     * 最小值
+     */
+    private BigDecimal minimum;
+
+    /**
+     * 时间格式
+     */
+    private String timeFormat;
+
+    /**
+     * 布尔值false
+     */
+    private String boolFalse;
+
+    /**
+     * 布尔值true
+     */
+    private String boolTrue;
+
+    /**
+     * 删除标识;0:未删除,1:已删除
+     */
+    private Integer deleteFlag;
+
+    /**
+     * 描述
+     */
+    private String attributeDescribe;
+
+    /**
+     * 创建人
+     */
+    private String createdBy;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdTime;
+
+    /**
+     * 更新人
+     */
+    private String updatedBy;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updatedTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+
+    /**
+     * 属性字典
+     */
+    private String attributeDict;
+
+
+}

+ 40 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/RuleEngine.java

@@ -0,0 +1,40 @@
+package com.usky.rule.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import retrofit2.http.Field;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * 对应表 rule_engine(数据规则引擎)
+ */
+@Data
+public class RuleEngine implements Serializable {
+    private Long id;
+    private Long projectId;
+    private String name;
+    private Long spaceId;
+    /** 规则状态 1:启用 0:停用 */
+    private Integer status;
+    private String descr;
+    /** JSON:规则详情(条件、动作等与前端约定) */
+    private String detail;
+    private String updatedBy;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+    private String createdBy;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+
+    @TableField(exist = false)
+    private String spaceName;
+}

+ 32 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/RuleEngineCondition.java

@@ -0,0 +1,32 @@
+package com.usky.rule.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * 规则引擎条件字典(表 rule_engine_condition),用于条件下拉
+ * type 1:触发条件 2:约束条件
+ */
+@Data
+public class RuleEngineCondition {
+    private Long id;
+    private String optionalCondition;
+    private String expression;
+    private String descr;
+    /** 类型 1:触发条件 2:约束条件 */
+    private Integer type;
+    private String updatedBy;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+    private String createdBy;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+}

+ 28 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/RuleEngineCron.java

@@ -0,0 +1,28 @@
+package com.usky.rule.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * 规则引擎 CRON(表 rule_engine_cron)
+ */
+@Data
+public class RuleEngineCron {
+    private Long id;
+    private Long ruleEngineId;
+    private String cron;
+    private String updatedBy;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+    private String createdBy;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+}

+ 30 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/RuleEngineDevice.java

@@ -0,0 +1,30 @@
+package com.usky.rule.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * 对应表 rule_engine_device(规则绑定设备+属性标识)
+ */
+@Data
+public class RuleEngineDevice {
+    private Long id;
+    private String deviceId;
+    private String identifier;
+    private Long ruleEngineId;
+    private Long productId;
+    private String updatedBy;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+    private String createdBy;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+}

+ 40 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/domain/RuleEngineLog.java

@@ -0,0 +1,40 @@
+package com.usky.rule.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * 对应表 rule_engine_log(规则执行日志)
+ */
+@Data
+public class RuleEngineLog {
+    private Long id;
+    private Long projectId;
+    private Long ruleEngineId;
+    private String ruleEngineName;
+    /** 自动触发 0:否 1:是 */
+    private Integer autoTrigger;
+    /** 触发类型 device/space/cron */
+    private String triggerType;
+    /** 执行动作 deviceControl/alarmEvent/workOrder */
+    private String action;
+    /** 日志数据 JSON */
+    private String detail;
+    private String content;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date time;
+    private String updatedBy;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+    private String createdBy;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+}

+ 62 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/engine/RuleConditionEvaluator.java

@@ -0,0 +1,62 @@
+package com.usky.rule.engine;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+
+/**
+ * 规则 detail JSON 条件评估(简化版):支持 threshold 比较。
+ * 与 leo-rule-engine 中按 identifier + 表达式匹配的思路一致。
+ */
+public final class RuleConditionEvaluator {
+
+    private RuleConditionEvaluator() {
+    }
+
+    /**
+     * @param detailJson rule_engine.detail
+     * @param value      当前属性值
+     */
+    public static boolean evaluate(String detailJson, Object value) {
+        if (detailJson == null || detailJson.isEmpty()) {
+            return false;
+        }
+        try {
+            JSONObject root = JSON.parseObject(detailJson);
+            if (root.containsKey("threshold")) {
+                double v = toDouble(value);
+                double th = root.getDoubleValue("threshold");
+                String op = root.getString("op");
+                if (op == null) {
+                    op = ">=";
+                }
+                switch (op) {
+                    case ">":
+                        return v > th;
+                    case ">=":
+                        return v >= th;
+                    case "<":
+                        return v < th;
+                    case "<=":
+                        return v <= th;
+                    case "==":
+                        return Double.compare(v, th) == 0;
+                    default:
+                        return v >= th;
+                }
+            }
+            return root.getBooleanValue("match");
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    private static double toDouble(Object value) {
+        if (value == null) {
+            return Double.NaN;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).doubleValue();
+        }
+        return Double.parseDouble(String.valueOf(value));
+    }
+}

+ 30 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/enums/ActionTypeEnum.java

@@ -0,0 +1,30 @@
+package com.usky.rule.enums;
+
+import com.usky.rule.exception.BizException;
+import java.util.Objects;
+
+public enum ActionTypeEnum {
+    ALARM_EVENT("alarmEvent"),
+    WORK_ORDER("workOrder"),
+    DEVICE_CONTROL("deviceControl");
+
+    private final String type;
+
+    private ActionTypeEnum(String type) {
+        this.type = type;
+    }
+
+    public static ActionTypeEnum getTypeEnum(String type) {
+        for(ActionTypeEnum typeEnum : values()) {
+            if (Objects.equals(typeEnum.getType(), type)) {
+                return typeEnum;
+            }
+        }
+
+        throw new BizException("不支持该类型的执行动作");
+    }
+
+    public String getType() {
+        return this.type;
+    }
+}

+ 28 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/enums/ConstraintTypeEnum.java

@@ -0,0 +1,28 @@
+package com.usky.rule.enums;
+
+import java.util.Objects;
+
+public enum ConstraintTypeEnum {
+    CRON("cron"),
+    DEVICE("device");
+
+    private final String type;
+
+    private ConstraintTypeEnum(String type) {
+        this.type = type;
+    }
+
+    public static ConstraintTypeEnum getTypeEnum(String type) {
+        for(ConstraintTypeEnum typeEnum : values()) {
+            if (Objects.equals(typeEnum.getType(), type)) {
+                return typeEnum;
+            }
+        }
+
+        return null;
+    }
+
+    public String getType() {
+        return this.type;
+    }
+}

+ 29 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/enums/TimeTypeEnum.java

@@ -0,0 +1,29 @@
+package com.usky.rule.enums;
+
+public enum TimeTypeEnum {
+    HOUR("hour"),
+    DAY("day"),
+    WEEK("week"),
+    MONTH("month"),
+    YEAR("year");
+
+    private final String identifier;
+
+    public String getIdentifier() {
+        return this.identifier;
+    }
+
+    private TimeTypeEnum(String identifier) {
+        this.identifier = identifier;
+    }
+
+    public static TimeTypeEnum get(String identifier) {
+        for(TimeTypeEnum timeTypeEnum : values()) {
+            if (timeTypeEnum.getIdentifier().equals(identifier)) {
+                return timeTypeEnum;
+            }
+        }
+
+        throw new IllegalArgumentException("不支持的时间类型");
+    }
+}

+ 27 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/enums/TriggerTypeEnum.java

@@ -0,0 +1,27 @@
+package com.usky.rule.enums;
+
+public enum TriggerTypeEnum {
+    CRON("cron"),
+    SPACE("space"),
+    DEVICE("device");
+
+    private final String type;
+
+    private TriggerTypeEnum(String type) {
+        this.type = type;
+    }
+
+    public static TriggerTypeEnum getTypeEnum(String type) {
+        for(TriggerTypeEnum typeEnum : values()) {
+            if (typeEnum.getType().equals(type)) {
+                return typeEnum;
+            }
+        }
+
+        return null;
+    }
+
+    public String getType() {
+        return this.type;
+    }
+}

+ 22 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/enums/TriggerValueTypeEnum.java

@@ -0,0 +1,22 @@
+package com.usky.rule.enums;
+
+public enum TriggerValueTypeEnum {
+    ACQ("采集值", "acq"),
+    CONSUMPTION("用量", "consumption");
+
+    private final String name;
+    private final String value;
+
+    private TriggerValueTypeEnum(String name, String value) {
+        this.name = name;
+        this.value = value;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getValue() {
+        return this.value;
+    }
+}

+ 25 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/exception/BizException.java

@@ -0,0 +1,25 @@
+package com.usky.rule.exception;
+
+import org.springframework.http.HttpStatus;
+
+public class BizException extends RuntimeException {
+    private final String code;
+
+    public BizException(String message) {
+        super(message);
+        this.code = "200001";
+    }
+
+    public BizException(String code, String message) {
+        super(message);
+        this.code = code;
+    }
+
+    public String getCode() {
+        return this.code;
+    }
+
+    public HttpStatus getHttpStatus() {
+        return HttpStatus.OK;
+    }
+}

+ 40 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/jobs/CommonJob.java

@@ -0,0 +1,40 @@
+package com.usky.rule.jobs;
+
+import com.usky.rule.enums.TriggerTypeEnum;
+import com.usky.rule.vo.action.RuleEngineAction;
+import com.usky.rule.vo.log.RuleEngineDetailLog;
+import com.usky.rule.listeners.CommonListener;
+import com.usky.rule.util.RuleEngineUtil;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Resource;
+import org.quartz.CronTrigger;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.Job;
+import org.quartz.JobDataMap;
+import org.quartz.JobExecutionContext;
+
+@DisallowConcurrentExecution
+public class CommonJob implements Job {
+    @Resource
+    private RuleEngineUtil ruleEngineUtil;
+
+    public CommonJob() {
+    }
+
+    public void execute(JobExecutionContext context) {
+        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
+        Long ruleEngineId = (Long)dataMap.get("ruleEngineId");
+        Long projectId = (Long)dataMap.get("projectId");
+        Long spaceId = (Long)dataMap.get("spaceId");
+        List<RuleEngineAction> actions = (List)dataMap.get("actions");
+        RuleEngineDetailLog detail = (RuleEngineDetailLog)context.get("detail");
+        if (detail == null) {
+            detail = CommonListener.initRuleEngineDetailLog((CronTrigger)context.getTrigger(), LocalDateTime.now());
+            detail.setConstraints(new ArrayList());
+        }
+
+//        this.ruleEngineUtil.performMultipleDevicesControl(ruleEngineId, true, TriggerTypeEnum.CRON.getType(), projectId, spaceId, actions, detail);
+    }
+}

+ 182 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/jobs/ConsumptionJob.java

@@ -0,0 +1,182 @@
+package com.usky.rule.jobs;
+
+
+import com.usky.common.security.utils.SecurityUtils;
+import com.usky.rule.domain.RuleEngine;
+import com.usky.rule.vo.Result;
+import com.usky.rule.util.JsonUtil;
+import com.usky.rule.enums.TimeTypeEnum;
+import com.usky.rule.enums.TriggerTypeEnum;
+import com.usky.rule.enums.TriggerValueTypeEnum;
+import com.usky.rule.vo.DataPointVO;
+//import com.leo.model.vo.DeviceFunctionDataVO;
+import com.usky.rule.vo.Condition;
+import com.usky.rule.vo.Expression;
+import com.usky.rule.vo.RuleEngineDetail;
+import com.usky.rule.vo.TimeRange;
+import com.usky.rule.vo.action.RuleEngineAction;
+import com.usky.rule.vo.constraint.DeviceConstraint;
+import com.usky.rule.vo.log.RuleEngineDetailLog;
+import com.usky.rule.vo.trigger.DeviceTrigger;
+import com.usky.rule.vo.visualization.SimpleVO;
+import com.usky.rule.subscribe.TriggerDeviceUtil;
+import com.usky.rule.util.RuleEngineUtil;
+//import com.usky.rule.DeviceService;
+import com.usky.rule.service.RuleEngineService;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Resource;
+import org.cache2k.Cache;
+import org.cache2k.CacheEntry;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+
+@DisallowConcurrentExecution
+public class ConsumptionJob implements Job {
+    @Resource
+    private Cache<Long, List<DeviceTrigger>> consumptionTriggerCache;
+    @Resource
+    private TriggerDeviceUtil triggerDeviceUtil;
+    @Resource
+    private RuleEngineUtil ruleEngineUtil;
+//    @Resource
+//    private DeviceService deviceService;
+    @Resource
+    private RuleEngineService ruleEngineService;
+
+    public ConsumptionJob() {
+    }
+
+    public void execute(JobExecutionContext context) throws JobExecutionException {
+        LocalDateTime[] times = new LocalDateTime[2];
+        LocalDateTime now = LocalDateTime.now();
+
+        for(CacheEntry<Long, List<DeviceTrigger>> entry : this.consumptionTriggerCache.entries()) {
+            Long engineId = (Long)entry.getKey();
+            RuleEngine ruleEngine = (RuleEngine)this.ruleEngineService.getById(engineId);
+            Long spaceId = ruleEngine.getSpaceId();
+            RuleEngineDetail ruleEngineDetail = (RuleEngineDetail)JsonUtil.toObject(ruleEngine.getDetail(), RuleEngineDetail.class);
+            List<RuleEngineAction> actions = this.ruleEngineService.getActions(ruleEngineDetail.getActions());
+            if (!actions.isEmpty()) {
+                RuleEngineDetailLog ruleEngineDetailLog = new RuleEngineDetailLog();
+                boolean spaceTriggerAction = false;
+
+                label77:
+                for(DeviceTrigger trigger : entry.getValue()) {
+                    if (!CollectionUtils.isEmpty(trigger.getDevices())) {
+                        label75:
+                        for(SimpleVO device : trigger.getDevices()) {
+                            List<Condition> meetTriggerConditionList = new ArrayList();
+                            List<Condition> triggerConditions = trigger.getConditions();
+                            StringBuilder boolConstraintExp = new StringBuilder();
+                            Map<String, String> valueMap = new HashMap();
+                            boolConstraintExp.append("(");
+
+                            for(int i = 0; i < triggerConditions.size(); ++i) {
+                                Condition triggerCondition = (Condition)triggerConditions.get(i);
+                                String identifier = triggerCondition.getIdentifier();
+                                Expression expression = triggerCondition.getExpression();
+                                TimeRange timeRange = triggerCondition.getTimeRange();
+                                String valueCondition = triggerCondition.getCondition();
+                                String operator = triggerCondition.getOperator();
+                                if (i != 0 && preAssertFalse(boolConstraintExp, operator)) {
+                                    continue label75;
+                                }
+
+                                initStartTimeAndEndTime(now, times, timeRange);
+//                                Result<DeviceFunctionDataVO> result = this.deviceService.getTimeRangeFunctionData(device.getId(), identifier, times[0], times[1]);
+//                                DeviceFunctionDataVO data = (DeviceFunctionDataVO)result.getData();
+//                                if (data == null) {
+//                                    boolConstraintExp.append(false);
+//                                } else {
+//                                    Map<String, List<DataPointVO>> values = data.getValues();
+//                                    List<DataPointVO> dataPointVOList = (List)values.get(device.getId());
+//                                    if (dataPointVOList != null && !dataPointVOList.isEmpty()) {
+//                                        DataPointVO first = (DataPointVO)dataPointVOList.get(0);
+//                                        DataPointVO last = (DataPointVO)dataPointVOList.get(dataPointVOList.size() - 1);
+//                                        BigDecimal currValue = last.getValue().subtract(first.getValue());
+//                                        boolean meetCondition = TriggerDeviceUtil.isMeetConsumptionCondition(currValue, valueCondition, expression.getX());
+//                                        if (meetCondition) {
+//                                            meetTriggerConditionList.add(triggerCondition);
+//                                            valueMap.put(identifier, currValue.toString());
+//                                        }
+//
+//                                        boolConstraintExp.append(meetCondition);
+//                                    } else {
+//                                        boolConstraintExp.append(false);
+//                                    }
+//                                }
+                            }
+
+                            spaceTriggerAction = TriggerDeviceUtil.getBooleanExpressionValue(boolConstraintExp + ")");
+                            if (spaceTriggerAction) {
+                                this.triggerDeviceUtil.setTriggerLog(now, ruleEngineDetailLog, device.getId(), TriggerValueTypeEnum.CONSUMPTION.getValue(), TriggerTypeEnum.DEVICE, meetTriggerConditionList, valueMap);
+                                break label77;
+                            }
+                        }
+                    }
+                }
+
+                if (spaceTriggerAction) {
+                    List<DeviceConstraint> deviceConstraints = this.ruleEngineService.getDeviceConstraints(ruleEngineDetail.getConstraints());
+                    boolean constraintAction = this.triggerDeviceUtil.meetConstraintAction(deviceConstraints, ruleEngineDetailLog);
+                    if (constraintAction) {
+//                        this.ruleEngineUtil.performMultipleDevicesControl(engineId, true, TriggerTypeEnum.DEVICE.getType(), ruleEngine.getProjectId(), spaceId, actions, ruleEngineDetailLog);
+                    }
+                }
+            }
+        }
+
+    }
+
+    public static void initStartTimeAndEndTime(LocalDateTime now, LocalDateTime[] times, TimeRange timeRange) {
+        Integer start = timeRange.getStart();
+        Integer end = timeRange.getEnd();
+        Assert.notNull(start, "start不能为空");
+        Assert.notNull(end, "end不能为空");
+        Assert.isTrue(end - start > 0, "结束时间必须大于起始时间");
+        LocalDateTime startTime = null;
+        LocalDateTime endTime = null;
+        switch (TimeTypeEnum.get(timeRange.getType())) {
+            case HOUR:
+                endTime = now.truncatedTo(ChronoUnit.HOURS);
+                int hour = endTime.getHour();
+                if (hour < start) {
+                    startTime = endTime.minusDays(1L).withHour(start);
+                } else {
+                    startTime = endTime.withHour(start);
+                }
+                break;
+            case DAY:
+            case WEEK:
+                endTime = now.truncatedTo(ChronoUnit.DAYS);
+                int day = endTime.getDayOfMonth();
+                if (day < start) {
+                    startTime = endTime.minusMonths(1L).withDayOfMonth(start);
+                } else {
+                    startTime = endTime.withDayOfMonth(start);
+                }
+        }
+
+        times[0] = startTime;
+        times[1] = endTime;
+    }
+
+    public static boolean preAssertFalse(StringBuilder boolConstraintExp, String operator) {
+        if (TriggerDeviceUtil.checkOperator(operator)) {
+            throw new RuntimeException("operator的值必须是 && or ||");
+        } else {
+            boolConstraintExp.append(" ").append(operator).append(" ");
+            return !TriggerDeviceUtil.getBooleanExpressionValue(boolConstraintExp + "true)");
+        }
+    }
+}

+ 94 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/listeners/CommonListener.java

@@ -0,0 +1,94 @@
+package com.usky.rule.listeners;
+
+import com.usky.rule.util.CronUtil;
+import com.usky.rule.util.DateTimeUtil;
+import com.usky.rule.enums.TriggerTypeEnum;
+import com.usky.rule.vo.constraint.CronConstraint;
+import com.usky.rule.vo.constraint.DeviceConstraint;
+import com.usky.rule.vo.log.BaseLog;
+import com.usky.rule.vo.log.CronTriggerLog;
+import com.usky.rule.vo.log.RuleEngineDetailLog;
+import com.usky.rule.subscribe.TriggerDeviceUtil;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.usky.rule.vo.log.CronTriggerLog;
+import com.usky.rule.vo.log.RuleEngineDetailLog;
+import org.quartz.CronTrigger;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.JobListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CommonListener implements JobListener {
+    private static final Logger LOGGER = LoggerFactory.getLogger(CommonListener.class);
+    private final String name;
+    private final TriggerDeviceUtil triggerDeviceUtil;
+    private final List<CronConstraint> cronConstraintList;
+    private final List<DeviceConstraint> deviceConstraintList;
+
+    public CommonListener(String name, TriggerDeviceUtil triggerDeviceUtil, List<CronConstraint> cronConstraintList, List<DeviceConstraint> deviceConstraints) {
+        this.name = name;
+        this.triggerDeviceUtil = triggerDeviceUtil;
+        this.cronConstraintList = cronConstraintList;
+        this.deviceConstraintList = deviceConstraints;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void jobToBeExecuted(JobExecutionContext context) {
+        LocalDateTime now = LocalDateTime.now();
+        List<BaseLog> constraintLogs = new ArrayList();
+        RuleEngineDetailLog ruleEngineDetailLog = initRuleEngineDetailLog((CronTrigger)context.getTrigger(), now);
+        ruleEngineDetailLog.setConstraints(constraintLogs);
+        LOGGER.info("Job {} is about to be executed", context.getJobDetail().getKey());
+        if (this.cronConstraintList != null) {
+            for(CronConstraint cronConstraint : this.cronConstraintList) {
+                if (CronUtil.isCronMatched(cronConstraint.getCron())) {
+                    throw new RuntimeException(" cron condition not met, aborting job execution.");
+                }
+
+                CronTriggerLog cronConstraintLog = new CronTriggerLog();
+                cronConstraintLog.setCronExp(cronConstraint.getCron());
+                cronConstraintLog.setTime(DateTimeUtil.format(now));
+                BaseLog cronBaseLog = new BaseLog();
+                cronBaseLog.setDetail(cronConstraintLog);
+                cronBaseLog.setType(TriggerTypeEnum.CRON.getType());
+                constraintLogs.add(cronBaseLog);
+            }
+        }
+
+        if (this.deviceConstraintList != null && this.triggerDeviceUtil.meetConstraintAction(this.deviceConstraintList, ruleEngineDetailLog)) {
+            throw new RuntimeException("device constraint condition not met, aborting job execution.");
+        } else {
+            context.put("detail", ruleEngineDetailLog);
+        }
+    }
+
+    public static RuleEngineDetailLog initRuleEngineDetailLog(CronTrigger trigger, LocalDateTime now) {
+        RuleEngineDetailLog ruleEngineDetailLog = new RuleEngineDetailLog();
+        List<BaseLog> triggerLogs = new ArrayList();
+        ruleEngineDetailLog.setTriggers(triggerLogs);
+        String cronExpression = trigger.getCronExpression();
+        CronTriggerLog cronTriggerLog = new CronTriggerLog();
+        cronTriggerLog.setCronExp(cronExpression);
+        cronTriggerLog.setTime(DateTimeUtil.format(now));
+        BaseLog baseLog = new BaseLog();
+        baseLog.setDetail(cronTriggerLog);
+        baseLog.setType(TriggerTypeEnum.CRON.getType());
+        triggerLogs.add(baseLog);
+        return ruleEngineDetailLog;
+    }
+
+    public void jobExecutionVetoed(JobExecutionContext context) {
+        LOGGER.info("Job {} was vetoed from being executed", context.getJobDetail().getKey());
+    }
+
+    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
+        LOGGER.info("Job {} was executed", context.getJobDetail().getKey());
+    }
+}

+ 16 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/BaseBuildMapper.java

@@ -0,0 +1,16 @@
+package com.usky.rule.mapper;
+
+import com.usky.rule.domain.BaseBuild;
+import com.usky.common.mybatis.core.CrudMapper;
+
+/**
+ * <p>
+ * 建筑信息 Mapper 接口
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+public interface BaseBuildMapper extends CrudMapper<BaseBuild> {
+
+}

+ 16 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/BaseSpaceMapper.java

@@ -0,0 +1,16 @@
+package com.usky.rule.mapper;
+
+import com.usky.rule.domain.BaseSpace;
+import com.usky.common.mybatis.core.CrudMapper;
+
+/**
+ * <p>
+ * 空间 Mapper 接口
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+public interface BaseSpaceMapper extends CrudMapper<BaseSpace> {
+
+}

+ 16 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/DmpDeviceMapper.java

@@ -0,0 +1,16 @@
+package com.usky.rule.mapper;
+
+import com.usky.rule.domain.DmpDevice;
+import com.usky.common.mybatis.core.CrudMapper;
+
+/**
+ * <p>
+ * 设备信息表 Mapper 接口
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+public interface DmpDeviceMapper extends CrudMapper<DmpDevice> {
+
+}

+ 16 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/DmpProductAttributeMapper.java

@@ -0,0 +1,16 @@
+package com.usky.rule.mapper;
+
+import com.usky.rule.domain.DmpProductAttribute;
+import com.usky.common.mybatis.core.CrudMapper;
+
+/**
+ * <p>
+ * 产品属性表 Mapper 接口
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+public interface DmpProductAttributeMapper extends CrudMapper<DmpProductAttribute> {
+
+}

+ 16 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/DmpProductMapper.java

@@ -0,0 +1,16 @@
+package com.usky.rule.mapper;
+
+import com.usky.rule.domain.DmpProduct;
+import com.usky.common.mybatis.core.CrudMapper;
+
+/**
+ * <p>
+ * 产品信息表 Mapper 接口
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+public interface DmpProductMapper extends CrudMapper<DmpProduct> {
+
+}

+ 14 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/RuleEngineConditionMapper.java

@@ -0,0 +1,14 @@
+package com.usky.rule.mapper;
+
+import com.usky.rule.domain.RuleEngineCondition;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 规则条件字典,供条件下拉;tenantId 为空时不按租户过滤
+ */
+public interface RuleEngineConditionMapper {
+    List<RuleEngineCondition> selectAll(@Param("tenantId") Integer tenantId);
+    List<RuleEngineCondition> selectByType(@Param("type") Integer type, @Param("tenantId") Integer tenantId);
+}

+ 18 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/RuleEngineCronMapper.java

@@ -0,0 +1,18 @@
+package com.usky.rule.mapper;
+
+import com.usky.rule.domain.RuleEngineCron;
+import com.usky.rule.vo.RuleEngineCronVO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface RuleEngineCronMapper {
+    int insert(RuleEngineCron row);
+    int deleteByRuleEngineId(@Param("ruleEngineId") Long ruleEngineId);
+    List<RuleEngineCron> selectByRuleEngineId(@Param("ruleEngineId") Long ruleEngineId);
+
+    /**
+     * 查询所有已启用规则及其 cron(status=1),供定时任务调度使用(对应 leo getTurnedOnCron)
+     */
+    List<RuleEngineCronVO> getTurnedOnCron();
+}

+ 19 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/RuleEngineDeviceMapper.java

@@ -0,0 +1,19 @@
+package com.usky.rule.mapper;
+
+import com.usky.rule.domain.RuleEngineDevice;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface RuleEngineDeviceMapper {
+    /** tenantId 为空时不按租户过滤 */
+    List<RuleEngineDevice> selectByDeviceAndIdentifier(@Param("deviceId") String deviceId,
+                                                        @Param("identifier") String identifier,
+                                                        @Param("productId") Long productId,
+                                                        @Param("tenantId") Integer tenantId);
+    int insert(RuleEngineDevice row);
+    int deleteByRuleEngineId(@Param("ruleEngineId") Long ruleEngineId);
+    List<RuleEngineDevice> selectByRuleEngineId(@Param("ruleEngineId") Long ruleEngineId);
+    /** 按设备 ID 查询关联的规则绑定(供设备上报触发使用) */
+    List<RuleEngineDevice> selectByDeviceId(@Param("deviceId") String deviceId);
+}

+ 15 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/RuleEngineLogMapper.java

@@ -0,0 +1,15 @@
+package com.usky.rule.mapper;
+
+import com.usky.rule.domain.RuleEngineLog;
+import com.usky.rule.vo.RuleEngineLogPageRequest;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface RuleEngineLogMapper {
+    int insert(RuleEngineLog row);
+    int updateById(RuleEngineLog row);
+    int deleteById(@Param("id") Long id);
+    RuleEngineLog selectById(@Param("id") Long id);
+    List<RuleEngineLog> selectPage(RuleEngineLogPageRequest req);
+}

+ 19 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mapper/RuleEngineMapper.java

@@ -0,0 +1,19 @@
+package com.usky.rule.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.rule.domain.RuleEngine;
+import com.usky.rule.vo.RuleEnginePageRequest;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface RuleEngineMapper extends CrudMapper<RuleEngine> {
+    int insert(RuleEngine row);
+    int updateById(RuleEngine row);
+    int deleteById(@Param("id") Long id);
+    RuleEngine selectById(@Param("id") Long id, @Param("tenantId") Integer tenantId);
+    int updateStatus(@Param("id") Long id, @Param("status") Integer status);
+    /** 按项目启用规则,供 MQ 消费侧加载;tenantId 为空时不按租户过滤 */
+    List<RuleEngine> selectEnabledByProjectId(@Param("projectId") Long projectId, @Param("tenantId") Integer tenantId);
+    List<Long> recursiveAllChildrenNodeIds(Long parentId);
+}

+ 44 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mq/RuleReportConsumer.java

@@ -0,0 +1,44 @@
+package com.usky.rule.mq;
+
+import com.alibaba.fastjson.JSON;
+import com.usky.rule.mq.vo.DeviceReportPayload;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.rocketmq.spring.core.RocketMQListener;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * 订阅 RocketMQ 设备上报 topic,触发数据规则(对标 MQTTUtil 订阅后解析并投递规则引擎)。
+ * 配置:rocketmq.consumer.group / topic 与 bootstrap.yml 或 Nacos 中一致。
+ */
+@Slf4j
+@Component
+@RocketMQMessageListener(
+        consumerGroup = "${rocketmq.consumer.group}",
+        topic = "${rocketmq.consumer.topic}"
+)
+public class RuleReportConsumer implements RocketMQListener<String> {
+
+    private final RuleTriggerContext ruleTriggerContext;
+
+    @Value("${spring.application.name:service-rule}")
+    private String appName;
+
+    public RuleReportConsumer(RuleTriggerContext ruleTriggerContext) {
+        this.ruleTriggerContext = ruleTriggerContext;
+    }
+
+    @Override
+    public void onMessage(String message) {
+        log.debug("[{}] RuleReportConsumer received: {}", appName, message);
+        try {
+            DeviceReportPayload payload = JSON.parseObject(message, DeviceReportPayload.class);
+            if (payload != null) {
+                ruleTriggerContext.onDeviceReport(payload);
+            }
+        } catch (Exception e) {
+            log.error("RuleReportConsumer parse/handle error: {}", e.getMessage(), e);
+        }
+    }
+}

+ 84 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mq/RuleTriggerContext.java

@@ -0,0 +1,84 @@
+package com.usky.rule.mq;
+
+import com.alibaba.fastjson.JSON;
+import com.usky.rule.domain.RuleEngine;
+import com.usky.rule.domain.RuleEngineDevice;
+import com.usky.rule.domain.RuleEngineLog;
+import com.usky.rule.engine.RuleConditionEvaluator;
+import com.usky.rule.mapper.RuleEngineDeviceMapper;
+import com.usky.rule.service.RuleEngineLogService;
+import com.usky.rule.service.RuleEngineService;
+import com.usky.rule.mq.vo.DeviceReportPayload;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 对标 MQTTUtil + SimpleContext:统一入口处理上报,再按策略/规则分发。
+ * 此处为 RocketMQ 收到消息后,按 project + device + identifier 找启用规则并触发写日志。
+ */
+@Slf4j
+@Service
+public class RuleTriggerContext {
+    private final RuleEngineService ruleEngineService;
+    private final RuleEngineDeviceMapper ruleEngineDeviceMapper;
+    private final RuleEngineLogService ruleEngineLogService;
+
+    public RuleTriggerContext(RuleEngineService ruleEngineService,
+                              RuleEngineDeviceMapper ruleEngineDeviceMapper,
+                              RuleEngineLogService ruleEngineLogService) {
+        this.ruleEngineService = ruleEngineService;
+        this.ruleEngineDeviceMapper = ruleEngineDeviceMapper;
+        this.ruleEngineLogService = ruleEngineLogService;
+    }
+
+    /**
+     * 处理一条设备上报(与 MyConsumer.getResource 类似:解析后交给业务)
+     */
+    public void onDeviceReport(DeviceReportPayload payload) {
+        if (payload.getProjectId() == null || payload.getDeviceId() == null || payload.getIdentifier() == null) {
+            log.warn("rule skip: missing projectId/deviceId/identifier, payload={}", JSON.toJSONString(payload));
+            return;
+        }
+        List<RuleEngineDevice> bindings = ruleEngineDeviceMapper.selectByDeviceAndIdentifier(
+                payload.getDeviceId(), payload.getIdentifier(), payload.getProductId(), null);
+        if (bindings == null || bindings.isEmpty()) {
+            return;
+        }
+        for (RuleEngineDevice binding : bindings) {
+            RuleEngine rule = ruleEngineService.getById(binding.getRuleEngineId());
+            if (rule == null || rule.getStatus() == null || rule.getStatus() != 1) {
+                continue;
+            }
+            if (!RuleConditionEvaluator.evaluate(rule.getDetail(), payload.getValue())) {
+                continue;
+            }
+            RuleEngineLog logRow = new RuleEngineLog();
+            logRow.setProjectId(rule.getProjectId());
+            logRow.setRuleEngineId(rule.getId());
+            logRow.setRuleEngineName(rule.getName());
+            logRow.setAutoTrigger(1);
+            logRow.setTriggerType("device");
+            logRow.setAction(parseAction(rule.getDetail()));
+            logRow.setDetail(rule.getDetail());
+            logRow.setContent(JSON.toJSONString(payload));
+            logRow.setTime(payload.getTs() != null ? new Date(payload.getTs()) : new Date());
+            logRow.setTenantId(rule.getTenantId());
+            ruleEngineLogService.add(logRow);
+            log.info("rule triggered ruleEngineId={} name={}", rule.getId(), rule.getName());
+        }
+    }
+
+    private String parseAction(String detailJson) {
+        try {
+            if (detailJson == null) {
+                return null;
+            }
+            return JSON.parseObject(detailJson).getString("action");
+        } catch (Exception e) {
+            return null;
+        }
+    }
+}

+ 23 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/mq/vo/DeviceReportPayload.java

@@ -0,0 +1,23 @@
+package com.usky.rule.mq.vo;
+
+import lombok.Data;
+
+/**
+ * RocketMQ 设备上报消息体(与 MQTT 上报入 MQ 后结构对齐,供规则触发)。
+ * MQTTUtil 侧多为 topic 订阅后解析 JSON;此处统一为 MQ 字符串反序列化。
+ */
+@Data
+public class DeviceReportPayload {
+    /** 项目 ID */
+    private Long projectId;
+    /** 设备 ID(业务 device_id) */
+    private String deviceId;
+    /** 产品 ID */
+    private Long productId;
+    /** 属性标识,对应 rule_engine_device.identifier */
+    private String identifier;
+    /** 上报值(数值/字符串由 detail 内条件决定) */
+    private Object value;
+    /** 毫秒时间戳,可选 */
+    private Long ts;
+}

+ 78 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/schedule/RuleEngineCronScheduler.java

@@ -0,0 +1,78 @@
+package com.usky.rule.schedule;
+
+import com.usky.rule.mapper.RuleEngineCronMapper;
+import com.usky.rule.service.RuleEngineService;
+import com.usky.rule.vo.RuleEngineCronVO;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.CronExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 数据规则引擎定时任务调度器(参考能耗 leo-rule-engine)。
+ * 按分钟扫描已启用规则的 cron 配置(getTurnedOnCron),若当前时间满足 cron 则触发规则并写 rule_engine_log(trigger_type=cron)。
+ */
+@Slf4j
+@Component
+@ConditionalOnProperty(name = "rule-engine.cron-schedule.enabled", havingValue = "true", matchIfMissing = true)
+public class RuleEngineCronScheduler {
+
+    private final RuleEngineCronMapper ruleEngineCronMapper;
+    private final RuleEngineService ruleEngineService;
+
+    public RuleEngineCronScheduler(RuleEngineCronMapper ruleEngineCronMapper,
+                                   RuleEngineService ruleEngineService) {
+        this.ruleEngineCronMapper = ruleEngineCronMapper;
+        this.ruleEngineService = ruleEngineService;
+    }
+
+    /**
+     * 每分钟整秒执行一次,拉取所有已启用规则及其 cron,匹配当前时间则触发(对应 leo Quartz + getTurnedOnCron)
+     */
+    @Scheduled(cron = "0 * * * * ?")
+    public void runCronRules() {
+        List<RuleEngineCronVO> list;
+        try {
+            list = ruleEngineCronMapper.getTurnedOnCron();
+        } catch (Exception e) {
+            log.warn("getTurnedOnCron error", e);
+            return;
+        }
+        if (list == null || list.isEmpty()) {
+            return;
+        }
+        Date now = new Date();
+        for (RuleEngineCronVO vo : list) {
+            if (vo.getRuleEngineId() == null || vo.getCron() == null || vo.getCron().trim().isEmpty()) {
+                continue;
+            }
+            try {
+                String cronStr = normalizeCron(vo.getCron().trim());
+                CronExpression expression = new CronExpression(cronStr);
+                if (expression.isSatisfiedBy(now)) {
+                    ruleEngineService.triggerByCron(vo.getRuleEngineId());
+                    log.debug("rule cron triggered ruleEngineId={} cron={}", vo.getRuleEngineId(), vo.getCron());
+                }
+            } catch (Exception e) {
+                log.warn("cron parse or trigger error ruleEngineId={} cron={}", vo.getRuleEngineId(), vo.getCron(), e);
+            }
+        }
+    }
+
+    /** Quartz 为 6 段(秒 分 时 日 月 周);若为 5 段(分 时 日 月 周)则前补秒 0 */
+    private static String normalizeCron(String cron) {
+        if (cron == null) {
+            return "0 0 * * * ?";
+        }
+        String s = cron.trim();
+        int n = s.split("\\s+").length;
+        if (n == 5) {
+            return "0 " + s;
+        }
+        return s;
+    }
+}

+ 16 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/BaseBuildService.java

@@ -0,0 +1,16 @@
+package com.usky.rule.service;
+
+import com.usky.rule.domain.BaseBuild;
+import com.usky.common.mybatis.core.CrudService;
+
+/**
+ * <p>
+ * 建筑信息 服务类
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+public interface BaseBuildService extends CrudService<BaseBuild> {
+
+}

+ 16 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/BaseSpaceService.java

@@ -0,0 +1,16 @@
+package com.usky.rule.service;
+
+import com.usky.rule.domain.BaseSpace;
+import com.usky.common.mybatis.core.CrudService;
+
+/**
+ * <p>
+ * 空间 服务类
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+public interface BaseSpaceService extends CrudService<BaseSpace> {
+
+}

+ 16 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/DmpDeviceService.java

@@ -0,0 +1,16 @@
+package com.usky.rule.service;
+
+import com.usky.rule.domain.DmpDevice;
+import com.usky.common.mybatis.core.CrudService;
+
+/**
+ * <p>
+ * 设备信息表 服务类
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+public interface DmpDeviceService extends CrudService<DmpDevice> {
+
+}

+ 16 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/DmpProductAttributeService.java

@@ -0,0 +1,16 @@
+package com.usky.rule.service;
+
+import com.usky.rule.domain.DmpProductAttribute;
+import com.usky.common.mybatis.core.CrudService;
+
+/**
+ * <p>
+ * 产品属性表 服务类
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+public interface DmpProductAttributeService extends CrudService<DmpProductAttribute> {
+
+}

+ 16 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/DmpProductService.java

@@ -0,0 +1,16 @@
+package com.usky.rule.service;
+
+import com.usky.rule.domain.DmpProduct;
+import com.usky.common.mybatis.core.CrudService;
+
+/**
+ * <p>
+ * 产品信息表 服务类
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+public interface DmpProductService extends CrudService<DmpProduct> {
+
+}

+ 13 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineConditionService.java

@@ -0,0 +1,13 @@
+package com.usky.rule.service;
+
+import com.usky.rule.domain.RuleEngineCondition;
+
+import java.util.List;
+
+/**
+ * 规则条件字典,供条件下拉(规则引擎触发条件/约束条件)
+ */
+public interface RuleEngineConditionService {
+    /** 查询全部条件,或按类型:1 触发条件 2 约束条件,null 表示全部 */
+    List<RuleEngineCondition> listConditions(Integer type);
+}

+ 15 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineCronService.java

@@ -0,0 +1,15 @@
+package com.usky.rule.service;
+
+import com.usky.rule.vo.RuleEngineCronVO;
+
+import java.util.List;
+
+/**
+ * 规则引擎 Cron 配置服务(对应 leo RuleEngineCronService,供 TriggerCronTask 等使用)
+ */
+public interface RuleEngineCronService {
+    /**
+     * 查询所有已启用规则及其 cron(status=1)
+     */
+    List<RuleEngineCronVO> getTurnedOnCron();
+}

+ 13 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineDetailService.java

@@ -0,0 +1,13 @@
+package com.usky.rule.service;
+
+import com.usky.rule.vo.RuleEngineDTO;
+
+public interface RuleEngineDetailService {
+    Boolean save(RuleEngineDTO dto);
+
+    Boolean update(RuleEngineDTO dto);
+
+    Boolean remove(Long id);
+
+    Boolean manualPerformDeviceControl(Long id);
+}

+ 15 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineDeviceService.java

@@ -0,0 +1,15 @@
+package com.usky.rule.service;
+
+import com.usky.rule.vo.RuleEngineDeviceVO;
+
+import java.util.List;
+
+/**
+ * 规则引擎设备绑定服务(对应 leo RuleEngineDeviceService,供 TriggerDeviceUtil 等使用)
+ */
+public interface RuleEngineDeviceService {
+    /**
+     * 按设备 ID 查询关联的规则绑定列表
+     */
+    List<RuleEngineDeviceVO> getByDeviceId(String deviceId);
+}

+ 14 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineLogService.java

@@ -0,0 +1,14 @@
+package com.usky.rule.service;
+
+import com.usky.common.core.bean.CommonPage;
+import com.usky.rule.domain.RuleEngineLog;
+import com.usky.rule.vo.RuleEngineContent;
+import com.usky.rule.vo.RuleEngineLogPageRequest;
+
+public interface RuleEngineLogService {
+    void add(RuleEngineLog log);
+    void update(RuleEngineLog log);
+    void remove(Long id);
+    RuleEngineContent getById(Long id);
+    CommonPage<RuleEngineLog> page(RuleEngineLogPageRequest request);
+}

+ 56 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/RuleEngineService.java

@@ -0,0 +1,56 @@
+package com.usky.rule.service;
+
+import com.usky.common.core.bean.CommonPage;
+import com.usky.rule.domain.RuleEngine;
+import com.usky.rule.vo.RuleEngineConfigDTO;
+import com.usky.rule.vo.RuleEngineDTO;
+import com.usky.rule.vo.RuleEngineDetail;
+import com.usky.rule.vo.RuleEnginePageRequest;
+import com.usky.rule.vo.action.RuleEngineAction;
+import com.usky.rule.vo.constraint.CronConstraint;
+import com.usky.rule.vo.constraint.DeviceConstraint;
+import com.usky.rule.vo.trigger.CronTrigger;
+import com.usky.rule.vo.trigger.DeviceTrigger;
+import com.usky.rule.vo.trigger.SpaceTrigger;
+
+import java.util.List;
+
+public interface RuleEngineService {
+    void add(RuleEngine ruleEngine);
+    void update(RuleEngine ruleEngine);
+    void remove(Long id);
+    RuleEngine getById(Long id);
+    CommonPage<RuleEngine> pageList(RuleEnginePageRequest request);
+    /** 查询多条数据(条件同分页,不分页),对应 leo RuleEngineController.list */
+    List<RuleEngine> list(RuleEnginePageRequest request);
+    void updateStatus(Long id, Integer status);
+    List<RuleEngine> listEnabledByProjectId(Long projectId);
+
+    /** 配置规则:保存规则主体 + cron 列表 + 设备绑定(新增或更新) */
+    void ruleEngineConfig(RuleEngineDTO dto);
+
+    /** 获取规则完整配置(规则 + cron 列表 + 设备列表) */
+    RuleEngineConfigDTO getEngineConfig(Long ruleEngineId);
+
+    /** 手动触发规则:写 rule_engine_log,triggerType=manual,autoTrigger=0 */
+    void manualTrigger(Long ruleEngineId);
+
+    /** 定时触发规则:写 rule_engine_log,triggerType=cron,autoTrigger=1(供定时任务调度调用) */
+    void triggerByCron(Long ruleEngineId);
+
+    List<CronTrigger> getCronTriggers(List<RuleEngineDetail.CommonVO> commonVOList);
+
+    List<DeviceTrigger> getDeviceTriggers(List<RuleEngineDetail.CommonVO> commonVOList);
+
+    List<SpaceTrigger> getSpaceTriggers(List<RuleEngineDetail.CommonVO> commonVOList);
+
+    List<CronConstraint> getCronConstraints(List<RuleEngineDetail.CommonVO> commonVOList);
+
+    List<DeviceConstraint> getDeviceConstraints(List<RuleEngineDetail.CommonVO> commonVOList);
+
+    List<RuleEngineAction> getActions(List<RuleEngineDetail.CommonVO> commonVOList);
+
+    String getName(Long ruleEngineId);
+
+    String getDetail(Long ruleEngineId);
+}

+ 20 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/BaseBuildServiceImpl.java

@@ -0,0 +1,20 @@
+package com.usky.rule.service.impl;
+
+import com.usky.rule.domain.BaseBuild;
+import com.usky.rule.mapper.BaseBuildMapper;
+import com.usky.rule.service.BaseBuildService;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 建筑信息 服务实现类
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Service
+public class BaseBuildServiceImpl extends AbstractCrudService<BaseBuildMapper, BaseBuild> implements BaseBuildService {
+
+}

+ 20 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/BaseSpaceServiceImpl.java

@@ -0,0 +1,20 @@
+package com.usky.rule.service.impl;
+
+import com.usky.rule.domain.BaseSpace;
+import com.usky.rule.mapper.BaseSpaceMapper;
+import com.usky.rule.service.BaseSpaceService;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 空间 服务实现类
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Service
+public class BaseSpaceServiceImpl extends AbstractCrudService<BaseSpaceMapper, BaseSpace> implements BaseSpaceService {
+
+}

+ 20 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/DmpDeviceServiceImpl.java

@@ -0,0 +1,20 @@
+package com.usky.rule.service.impl;
+
+import com.usky.rule.domain.DmpDevice;
+import com.usky.rule.mapper.DmpDeviceMapper;
+import com.usky.rule.service.DmpDeviceService;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 设备信息表 服务实现类
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Service
+public class DmpDeviceServiceImpl extends AbstractCrudService<DmpDeviceMapper, DmpDevice> implements DmpDeviceService {
+
+}

+ 20 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/DmpProductAttributeServiceImpl.java

@@ -0,0 +1,20 @@
+package com.usky.rule.service.impl;
+
+import com.usky.rule.domain.DmpProductAttribute;
+import com.usky.rule.mapper.DmpProductAttributeMapper;
+import com.usky.rule.service.DmpProductAttributeService;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 产品属性表 服务实现类
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Service
+public class DmpProductAttributeServiceImpl extends AbstractCrudService<DmpProductAttributeMapper, DmpProductAttribute> implements DmpProductAttributeService {
+
+}

+ 20 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/DmpProductServiceImpl.java

@@ -0,0 +1,20 @@
+package com.usky.rule.service.impl;
+
+import com.usky.rule.domain.DmpProduct;
+import com.usky.rule.mapper.DmpProductMapper;
+import com.usky.rule.service.DmpProductService;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 产品信息表 服务实现类
+ * </p>
+ *
+ * @author zyj
+ * @since 2026-03-17
+ */
+@Service
+public class DmpProductServiceImpl extends AbstractCrudService<DmpProductMapper, DmpProduct> implements DmpProductService {
+
+}

+ 31 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineConditionServiceImpl.java

@@ -0,0 +1,31 @@
+package com.usky.rule.service.impl;
+
+import com.usky.common.security.utils.SecurityUtils;
+import com.usky.rule.domain.RuleEngineCondition;
+import com.usky.rule.mapper.RuleEngineConditionMapper;
+import com.usky.rule.service.RuleEngineConditionService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class RuleEngineConditionServiceImpl implements RuleEngineConditionService {
+    private final RuleEngineConditionMapper ruleEngineConditionMapper;
+
+    public RuleEngineConditionServiceImpl(RuleEngineConditionMapper ruleEngineConditionMapper) {
+        this.ruleEngineConditionMapper = ruleEngineConditionMapper;
+    }
+
+    @Override
+    public List<RuleEngineCondition> listConditions(Integer type) {
+        Integer tenantId = null;
+        try {
+            tenantId = SecurityUtils.getTenantId();
+        } catch (Exception ignored) {
+        }
+        if (type == null) {
+            return ruleEngineConditionMapper.selectAll(tenantId);
+        }
+        return ruleEngineConditionMapper.selectByType(type, tenantId);
+    }
+}

+ 22 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineCronServiceImpl.java

@@ -0,0 +1,22 @@
+package com.usky.rule.service.impl;
+
+import com.usky.rule.mapper.RuleEngineCronMapper;
+import com.usky.rule.service.RuleEngineCronService;
+import com.usky.rule.vo.RuleEngineCronVO;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class RuleEngineCronServiceImpl implements RuleEngineCronService {
+    private final RuleEngineCronMapper ruleEngineCronMapper;
+
+    public RuleEngineCronServiceImpl(RuleEngineCronMapper ruleEngineCronMapper) {
+        this.ruleEngineCronMapper = ruleEngineCronMapper;
+    }
+
+    @Override
+    public List<RuleEngineCronVO> getTurnedOnCron() {
+        return ruleEngineCronMapper.getTurnedOnCron();
+    }
+}

+ 109 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineDetailServiceImpl.java

@@ -0,0 +1,109 @@
+package com.usky.rule.service.impl;
+
+import com.usky.rule.domain.RuleEngine;
+import com.usky.rule.util.JsonUtil;
+import com.usky.rule.vo.RuleEngineDTO;
+import com.usky.rule.enums.ActionTypeEnum;
+import com.usky.rule.enums.TriggerTypeEnum;
+import com.usky.rule.vo.RuleEngineDetail;
+import com.usky.rule.vo.action.RuleEngineAction;
+import com.usky.rule.vo.constraint.CronConstraint;
+import com.usky.rule.vo.constraint.DeviceConstraint;
+import com.usky.rule.vo.log.RuleEngineDetailLog;
+import com.usky.rule.vo.trigger.CronTrigger;
+import com.usky.rule.cache.DeviceTriggerIncludeMinuteCache;
+import com.usky.rule.config.CronTaskManager;
+import com.usky.rule.crons.TriggerCronTask;
+import com.usky.rule.service.RuleEngineDetailService;
+import com.usky.rule.util.RuleEngineUtil;
+import com.usky.rule.service.RuleEngineService;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+@Service("ruleEngineDetailService")
+public class RuleEngineDetailServiceImpl implements RuleEngineDetailService {
+    private CronTaskManager cronTaskManager;
+    private TriggerCronTask triggerCronTask;
+    private RuleEngineService ruleEngineService;
+    private RuleEngineUtil ruleEngineUtil;
+    private DeviceTriggerIncludeMinuteCache historyRecordCache;
+
+    public Boolean save(RuleEngineDTO dto) {
+        if (dto.getStatus() != null && dto.getStatus() == 0) {
+            return true;
+        } else if (StringUtils.isBlank(dto.getDetail())) {
+            return true;
+        } else {
+            RuleEngineDetail ruleEngineDetail = (RuleEngineDetail)JsonUtil.toObject(dto.getDetail(), RuleEngineDetail.class);
+            List<RuleEngineAction> actions = this.ruleEngineService.getActions(ruleEngineDetail.getActions());
+            if (actions != null && !actions.isEmpty()) {
+                this.triggerCronTask.setDeviceConsumptionCache(dto.getId(), ruleEngineDetail);
+                this.createCronTask(dto, ruleEngineDetail, actions);
+                return true;
+            } else {
+                return true;
+            }
+        }
+    }
+
+    private void createCronTask(RuleEngineDTO dto, RuleEngineDetail ruleEngineDetail, List<RuleEngineAction> actions) {
+        List<CronTrigger> cronTriggers = this.ruleEngineService.getCronTriggers(ruleEngineDetail.getTriggers());
+        if (cronTriggers != null) {
+            List<CronConstraint> cronConstraints = this.ruleEngineService.getCronConstraints(ruleEngineDetail.getConstraints());
+            List<DeviceConstraint> deviceConstraints = this.ruleEngineService.getDeviceConstraints(ruleEngineDetail.getConstraints());
+            this.addRuleEngineJobs(dto.getId(), dto.getProjectId(), dto.getSpaceId(), actions, cronTriggers, cronConstraints, deviceConstraints);
+        }
+
+    }
+
+    private void addRuleEngineJobs(Long id, Long projectId, Long spaceId, List<RuleEngineAction> actions, List<CronTrigger> cronTriggers, List<CronConstraint> cronConstraints, List<DeviceConstraint> deviceConstraints) {
+        TriggerCronTask.addCronJob(id, projectId, spaceId, cronTriggers, cronConstraints, deviceConstraints, actions, this.cronTaskManager);
+    }
+
+    public Boolean update(RuleEngineDTO dto) {
+        return this.cronTaskManager.deleteAllJobsInJobGroup(dto.getId()) ? this.save(dto) : false;
+    }
+
+    public Boolean remove(Long id) {
+        Assert.notNull(id, "id不能为空");
+        this.historyRecordCache.deleteConditions(id);
+        return this.cronTaskManager.deleteAllJobsInJobGroup(id);
+    }
+
+    public Boolean manualPerformDeviceControl(Long id) {
+        RuleEngine ruleEngine = (RuleEngine)this.ruleEngineService.getById(id);
+        Assert.notNull(ruleEngine, "规则引擎不存在");
+        RuleEngineDetail ruleEngineDetail = getRuleEngineDetail(ruleEngine);
+        if (ruleEngineDetail == null) {
+            return true;
+        } else {
+            List<RuleEngineAction> actions = this.ruleEngineService.getActions(ruleEngineDetail.getActions());
+            if (actions != null && !actions.isEmpty()) {
+                List<RuleEngineAction> deviceControlActions = (List)actions.stream().filter((ruleEngineAction) -> ActionTypeEnum.DEVICE_CONTROL.getType().equals(ruleEngineAction.getType())).collect(Collectors.toList());
+                if (deviceControlActions.isEmpty()) {
+                    return true;
+                } else {
+//                    this.ruleEngineUtil.performMultipleDevicesControl(id, false, TriggerTypeEnum.DEVICE.getType(), ruleEngine.getProjectId(), (Long)null, actions, new RuleEngineDetailLog());
+                    return true;
+                }
+            } else {
+                return true;
+            }
+        }
+    }
+
+    private static RuleEngineDetail getRuleEngineDetail(RuleEngine ruleEngine) {
+        return StringUtils.isNotBlank(ruleEngine.getDetail()) ? (RuleEngineDetail)JsonUtil.toObject(ruleEngine.getDetail(), RuleEngineDetail.class) : null;
+    }
+
+    public RuleEngineDetailServiceImpl(final CronTaskManager cronTaskManager, final TriggerCronTask triggerCronTask, final RuleEngineService ruleEngineService, final RuleEngineUtil ruleEngineUtil, final DeviceTriggerIncludeMinuteCache historyRecordCache) {
+        this.cronTaskManager = cronTaskManager;
+        this.triggerCronTask = triggerCronTask;
+        this.ruleEngineService = ruleEngineService;
+        this.ruleEngineUtil = ruleEngineUtil;
+        this.historyRecordCache = historyRecordCache;
+    }
+}

+ 57 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineDeviceServiceImpl.java

@@ -0,0 +1,57 @@
+package com.usky.rule.service.impl;
+
+import com.usky.rule.domain.RuleEngineDevice;
+import com.usky.rule.mapper.RuleEngineDeviceMapper;
+import com.usky.rule.service.RuleEngineDeviceService;
+import com.usky.rule.vo.RuleEngineDeviceVO;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class RuleEngineDeviceServiceImpl implements RuleEngineDeviceService {
+    private final RuleEngineDeviceMapper ruleEngineDeviceMapper;
+
+    public RuleEngineDeviceServiceImpl(RuleEngineDeviceMapper ruleEngineDeviceMapper) {
+        this.ruleEngineDeviceMapper = ruleEngineDeviceMapper;
+    }
+
+    @Override
+    public List<RuleEngineDeviceVO> getByDeviceId(String deviceId) {
+//        if (deviceId == null || deviceId.trim().isEmpty()) {
+//            return List.of();
+//        }
+        List<RuleEngineDevice> list = ruleEngineDeviceMapper.selectByDeviceId(deviceId.trim());
+//        if (list == null) {
+//            return List.of();
+//        }
+        return list.stream().map(this::toVO).collect(Collectors.toList());
+    }
+
+    private RuleEngineDeviceVO toVO(RuleEngineDevice e) {
+        RuleEngineDeviceVO vo = new RuleEngineDeviceVO();
+        vo.setId(e.getId());
+        vo.setDeviceId(e.getDeviceId());
+        vo.setIdentifier(e.getIdentifier());
+        vo.setRuleEngineId(e.getRuleEngineId());
+        vo.setProductId(e.getProductId() != null ? e.getProductId() : null);
+        try {
+            vo.setUpdatedBy(e.getUpdatedBy() != null && !e.getUpdatedBy().isEmpty() ? Long.parseLong(e.getUpdatedBy()) : null);
+        } catch (NumberFormatException ignored) {
+            vo.setUpdatedBy(null);
+        }
+        try {
+            vo.setCreatedBy(e.getCreatedBy() != null && !e.getCreatedBy().isEmpty() ? Long.parseLong(e.getCreatedBy()) : null);
+        } catch (NumberFormatException ignored) {
+            vo.setCreatedBy(null);
+        }
+        if (e.getUpdateTime() != null) {
+            vo.setUpdateTime(e.getUpdateTime().toString());
+        }
+        if (e.getCreateTime() != null) {
+            vo.setCreateTime(e.getCreateTime().toString());
+        }
+        return vo;
+    }
+}

+ 125 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineLogServiceImpl.java

@@ -0,0 +1,125 @@
+package com.usky.rule.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.common.core.exception.BusinessException;
+import com.usky.rule.domain.RuleEngineLog;
+import com.usky.rule.mapper.RuleEngineLogMapper;
+import com.usky.rule.service.RuleEngineLogService;
+import com.usky.rule.util.JsonUtil;
+import com.usky.rule.vo.RuleEngineContent;
+import com.usky.rule.vo.RuleEngineLogPageRequest;
+import com.usky.rule.vo.log.*;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class RuleEngineLogServiceImpl implements RuleEngineLogService {
+    private final RuleEngineLogMapper ruleEngineLogMapper;
+
+    private final Map<String, RuleEngineLogSerialization<?>> serializationMap = new HashMap();
+
+    public RuleEngineLogServiceImpl(RuleEngineLogMapper ruleEngineLogMapper) {
+        this.ruleEngineLogMapper = ruleEngineLogMapper;
+    }
+
+    @PostConstruct
+    public void initSerializationMap() {
+        TriggerCronSerialization triggerCronSerialization = new TriggerCronSerialization();
+        TriggerDeviceSerialization triggerDeviceSerialization = new TriggerDeviceSerialization();
+        this.serializationMap.put(triggerDeviceSerialization.getType(), triggerDeviceSerialization);
+        this.serializationMap.put(triggerCronSerialization.getType(), triggerCronSerialization);
+        ConstraintCronSerialization constraintCronSerialization = new ConstraintCronSerialization();
+        ConstraintDeviceSerialization constraintDeviceSerialization = new ConstraintDeviceSerialization();
+        this.serializationMap.put(constraintDeviceSerialization.getType(), constraintDeviceSerialization);
+        this.serializationMap.put(constraintCronSerialization.getType(), constraintCronSerialization);
+//        ActionAlarmEventSerialization actionAlarmEventSerialization = new ActionAlarmEventSerialization();
+//        ActionWorkOrderSerialization actionWorkOrderSerialization = new ActionWorkOrderSerialization();
+        ActionControlSerialization actionControlSerialization = new ActionControlSerialization();
+        this.serializationMap.put(actionControlSerialization.getType(), actionControlSerialization);
+//        this.serializationMap.put(actionAlarmEventSerialization.getType(), actionAlarmEventSerialization);
+//        this.serializationMap.put(actionWorkOrderSerialization.getType(), actionWorkOrderSerialization);
+    }
+
+    @Override
+    public void add(RuleEngineLog log) {
+        ruleEngineLogMapper.insert(log);
+    }
+
+    @Override
+    public void update(RuleEngineLog log) {
+        ruleEngineLogMapper.updateById(log);
+    }
+
+    @Override
+    public void remove(Long id) {
+        ruleEngineLogMapper.deleteById(id);
+    }
+
+    @Override
+    public RuleEngineContent getById(Long id) {
+        RuleEngineLog ruleEngineLog =  ruleEngineLogMapper.selectById(id);
+        if(ruleEngineLog==null){
+            return null;
+        }
+
+        String detail = ruleEngineLog.getDetail();
+        RuleEngineDetailLog ruleEngineDetailLog = (RuleEngineDetailLog) JsonUtil.toObject(detail, RuleEngineDetailLog.class);
+        List<BaseLog> triggerLogs = ruleEngineDetailLog.getTriggers();
+        List<BaseLog> constraintLogs = ruleEngineDetailLog.getConstraints();
+        List<BaseLog> actionLogs = ruleEngineDetailLog.getActions();
+        RuleEngineContent ruleEngineContent = new RuleEngineContent();
+        RuleEngineContent.Trigger trigger = new RuleEngineContent.Trigger();
+        trigger.setAutoTrigger(ruleEngineLog.getAutoTrigger() + "");
+        trigger.setType(ruleEngineLog.getTriggerType());
+        if (!CollectionUtils.isEmpty(triggerLogs)) {
+            BaseLog triggerLog = (BaseLog)triggerLogs.get(0);
+            Content triggerContent = ((RuleEngineLogSerialization)this.serializationMap.get("trigger:" + triggerLog.getType())).toContent(triggerLog.getDetail());
+            trigger.setContent(triggerContent.getContent());
+            trigger.setTime(triggerContent.getTime());
+        }
+
+        ruleEngineContent.setTrigger(trigger);
+        List<RuleEngineContent.Constraints> constraints = new ArrayList();
+        if (!CollectionUtils.isEmpty(constraintLogs)) {
+            for(BaseLog constraintLog : constraintLogs) {
+                RuleEngineContent.Constraints constraint = new RuleEngineContent.Constraints();
+                Content constraintContent = ((RuleEngineLogSerialization)this.serializationMap.get("constraint:" + constraintLog.getType())).toContent(constraintLog.getDetail());
+                constraint.setContent(constraintContent.getContent());
+                constraint.setType(constraintLog.getType());
+                constraints.add(constraint);
+            }
+        }
+
+        ruleEngineContent.setConstraints(constraints);
+        List<RuleEngineContent.Actions> actionsList = new ArrayList();
+        if (!CollectionUtils.isEmpty(actionLogs)) {
+            for(BaseLog actionLog : actionLogs) {
+                RuleEngineContent.Actions action = new RuleEngineContent.Actions();
+                Content actionContent = ((RuleEngineLogSerialization)this.serializationMap.get("action:" + actionLog.getType())).toContent(actionLog.getDetail());
+                action.setContent(actionContent.getContent());
+                action.setTime(actionContent.getTime());
+                action.setType(actionLog.getType());
+                actionsList.add(action);
+            }
+        }
+
+        ruleEngineContent.setActions(actionsList);
+        return ruleEngineContent;
+    }
+
+    @Override
+    public CommonPage<RuleEngineLog> page(RuleEngineLogPageRequest request) {
+        PageHelper.startPage(request.getPageNum(), request.getPageSize());
+        List<RuleEngineLog> list = ruleEngineLogMapper.selectPage(request);
+        PageInfo<RuleEngineLog> pageInfo = new PageInfo<>(list);
+        return new CommonPage<>(list, pageInfo.getTotal(), request.getPageSize(), request.getPageNum());
+    }
+}

+ 643 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/service/impl/RuleEngineServiceImpl.java

@@ -0,0 +1,643 @@
+package com.usky.rule.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import com.usky.common.security.utils.SecurityUtils;
+import com.usky.rule.cache.DeviceTriggerIncludeMinuteCache;
+import com.usky.rule.config.CronTaskManager;
+import com.usky.rule.domain.*;
+import com.usky.rule.enums.ActionTypeEnum;
+import com.usky.rule.enums.ConstraintTypeEnum;
+import com.usky.rule.enums.TriggerTypeEnum;
+import com.usky.rule.exception.BizException;
+import com.usky.rule.mapper.RuleEngineCronMapper;
+import com.usky.rule.mapper.RuleEngineDeviceMapper;
+import com.usky.rule.mapper.RuleEngineLogMapper;
+import com.usky.rule.mapper.RuleEngineMapper;
+import com.usky.rule.service.*;
+import com.usky.rule.util.CronUtil;
+import com.usky.rule.util.JsonUtil;
+import com.usky.rule.vo.*;
+import com.usky.rule.vo.action.DeviceControlAction;
+import com.usky.rule.vo.action.RuleEngineAction;
+import com.usky.rule.vo.constraint.CronConstraint;
+import com.usky.rule.vo.constraint.DeviceConstraint;
+import com.usky.rule.vo.trigger.CronTrigger;
+import com.usky.rule.vo.trigger.DeviceTrigger;
+import com.usky.rule.vo.trigger.SpaceTrigger;
+import com.usky.rule.vo.visualization.SimpleVO;
+import feign.Util;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.PostConstruct;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class RuleEngineServiceImpl extends AbstractCrudService<RuleEngineMapper, RuleEngine> implements RuleEngineService {
+    private final RuleEngineCronService ruleEngineCronService;
+    private final RuleEngineDeviceService ruleEngineDeviceService;
+    private static final Map<String, Class<?>> triggerMap = new HashMap();
+    private static final Map<String, Class<?>> constraintMap = new HashMap();
+    private static final Map<String, Class<?>> actionMap = new HashMap();
+    @Autowired
+    private RuleEngineLogService ruleEngineLogService;
+    @Autowired
+    private RuleEngineCronMapper ruleEngineCronMapper;
+    @Autowired
+    private RuleEngineDeviceMapper ruleEngineDeviceMapper;
+    @Autowired
+    private DeviceTriggerIncludeMinuteCache deviceTriggerIncludeMinuteCache;
+    @Autowired
+    private CronTaskManager cronTaskManager;
+    @Lazy
+    @Autowired
+    private RuleEngineDetailService ruleEngineDetailService;
+    @Autowired
+    private BaseSpaceService baseSpaceService;
+
+    public RuleEngineServiceImpl(RuleEngineCronService ruleEngineCronService, RuleEngineDeviceService ruleEngineDeviceService) {
+        this.ruleEngineCronService = ruleEngineCronService;
+        this.ruleEngineDeviceService = ruleEngineDeviceService;
+    }
+
+
+
+    @PostConstruct
+    public void init() {
+
+        triggerMap.put(TriggerTypeEnum.CRON.getType(), CronTrigger.class);
+        triggerMap.put(TriggerTypeEnum.DEVICE.getType(), DeviceTrigger.class);
+        triggerMap.put(TriggerTypeEnum.SPACE.getType(), SpaceTrigger.class);
+        constraintMap.put(ConstraintTypeEnum.CRON.getType(), CronConstraint.class);
+        constraintMap.put(ConstraintTypeEnum.DEVICE.getType(), DeviceConstraint.class);
+//        actionMap.put(ActionTypeEnum.ALARM_EVENT.getType(), AlarmEventAction.class);
+//        actionMap.put(ActionTypeEnum.WORK_ORDER.getType(), WorkOrderAction.class);
+        actionMap.put(ActionTypeEnum.DEVICE_CONTROL.getType(), DeviceControlAction.class);
+    }
+
+    @Override
+    public void add(RuleEngine ruleEngine) {
+        if (ruleEngine.getStatus() == null) {
+            ruleEngine.setStatus(0);
+        }
+        ruleEngine.setCreatedBy(SecurityUtils.getUsername());
+        ruleEngine.setTenantId(SecurityUtils.getTenantId());
+        getBaseMapper().insert(ruleEngine);
+    }
+
+    @Override
+    public void update(RuleEngine ruleEngine) {
+        ruleEngine.setUpdatedBy(SecurityUtils.getUsername());
+        getBaseMapper().updateById(ruleEngine);
+    }
+
+    /**
+     * 删除规则(参考 leo RuleEngineDetailServiceImpl.remove):
+     * 1. 校验 id 非空
+     * 2. 清理该规则下的 cron 任务数据(对应 leo cronTaskManager.deleteAllJobsInJobGroup)
+     * 3. 清理该规则关联的设备绑定(对应 leo historyRecordCache.deleteConditions 的关联数据)
+     * 4. 删除规则主表
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void remove(Long id) {
+        if (id == null) {
+            throw new IllegalArgumentException("id不能为空");
+        }
+
+        deviceTriggerIncludeMinuteCache.deleteConditions(id);
+        cronTaskManager.deleteAllJobsInJobGroup(id);
+        ruleEngineCronMapper.deleteByRuleEngineId(id);
+        ruleEngineDeviceMapper.deleteByRuleEngineId(id);
+        getBaseMapper().deleteById(id);
+    }
+
+    @Override
+    public RuleEngine getById(Long id) {
+        return getBaseMapper().selectById(id, SecurityUtils.getTenantId());
+    }
+
+    @Override
+    public CommonPage<RuleEngine> pageList(RuleEnginePageRequest request) {
+        List<Long> ids = new ArrayList();
+        if (request.getSpaceId() != null) {
+            ids.add(request.getSpaceId());
+            ids.addAll(getBaseMapper().recursiveAllChildrenNodeIds(request.getSpaceId()));
+        }
+
+
+        IPage<RuleEngine> page = new Page<>(request.getPageNum(), request.getPageSize());
+        LambdaQueryWrapper<RuleEngine> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.in(CollectionUtils.isNotEmpty(ids), RuleEngine::getSpaceId, ids)
+                .like(StringUtils.isNotBlank(request.getName()),RuleEngine::getName, request.getName())
+                .eq(request.getStatus() != null,RuleEngine::getStatus,request.getStatus())
+                .eq(RuleEngine::getTenantId,SecurityUtils.getTenantId())
+                .orderByDesc(RuleEngine::getId);
+        page = this.page(page, queryWrapper);
+
+        List<Long> spaceIds = page.getRecords().stream().map(RuleEngine::getSpaceId).distinct().collect(Collectors.toList());
+        List<BaseSpace> spaceList = baseSpaceService.listByIds(spaceIds);
+        Map<Long, BaseSpace> spaceMap = (Map)spaceList.stream().collect(Collectors.toMap(BaseSpace::getId, (s) -> s));
+        page.getRecords().forEach((r) -> r.setSpaceName(((BaseSpace)spaceMap.get(r.getSpaceId())).getName()));
+
+        return new CommonPage<>(page.getRecords(), page.getTotal(), request.getPageSize(), request.getPageNum());
+
+    }
+
+    @Override
+    public List<RuleEngine> list(RuleEnginePageRequest request) {
+        List<Long> ids = new ArrayList();
+        if (request.getSpaceId() != null) {
+            ids.add(request.getSpaceId());
+            ids.addAll(getBaseMapper().recursiveAllChildrenNodeIds(request.getSpaceId()));
+        }
+
+        LambdaQueryWrapper<RuleEngine> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.in(CollectionUtils.isNotEmpty(ids), RuleEngine::getSpaceId, ids)
+                .like(StringUtils.isNotBlank(request.getName()),RuleEngine::getName, request.getName())
+                .eq(request.getStatus() != null,RuleEngine::getStatus,request.getStatus())
+                .eq(RuleEngine::getTenantId,SecurityUtils.getTenantId());
+        List<RuleEngine> ruleEngines = this.list(queryWrapper);
+        return ruleEngines;
+    }
+
+    @Override
+    public void updateStatus(Long id, Integer status) {
+        LambdaQueryWrapper<RuleEngine> wrapper = Wrappers.lambdaQuery();
+        wrapper.eq(RuleEngine::getId, id)
+                .eq(RuleEngine::getTenantId,SecurityUtils.getTenantId());
+        RuleEngine engine = (RuleEngine)this.getOne(wrapper);
+        RuleEngineDTO engineDTO = new RuleEngineDTO();
+        engineDTO.setId(id);
+        engineDTO.setStatus(engine.getStatus() == 1 ? 0 : 1);
+        engineDTO.setProjectId(engine.getProjectId());
+        engineDTO.setName(engine.getName());
+        engineDTO.setDetail(engine.getDetail());
+        getBaseMapper().updateStatus(id, status);
+
+        ruleEngineDetailService.update(engineDTO);
+    }
+
+    @Override
+    public List<RuleEngine> listEnabledByProjectId(Long projectId) {
+        Integer tenantId = null;
+        try {
+            tenantId = SecurityUtils.getTenantId();
+        } catch (Exception ignored) {
+        }
+        return getBaseMapper().selectEnabledByProjectId(projectId, tenantId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void ruleEngineConfig(RuleEngineDTO dto) {
+        if (dto.getId() == null) {
+            throw new BizException("id不能为空");
+        }
+        RuleEngine rule = this.getById(dto.getId());
+        if (rule != null) {
+            String detail = dto.getDetail();
+
+            RuleEngineDetail ruleEngineDetail;
+            rule.setDetail(detail);
+            rule.setUpdateTime(LocalDateTime.now());
+            try {
+                ruleEngineDetail = JsonUtil.toObject(detail, RuleEngineDetail.class);
+            } catch (Exception var13) {
+                throw new BizException("json格式有误");
+            }
+
+            this.verifyJson(ruleEngineDetail);
+            this.updateById(rule);
+
+            Long ruleEngineId = rule.getId();
+            Integer tenantId = rule.getTenantId();
+
+            ruleEngineCronMapper.deleteByRuleEngineId(ruleEngineId);
+            List<RuleEngineDetail.CommonVO> triggers = ruleEngineDetail.getTriggers();
+            List<CronTrigger> cronTriggers = this.getCronTriggers(triggers);
+            if (cronTriggers != null && !cronTriggers.isEmpty()) {
+                cronTriggers.forEach((c) -> {
+                    RuleEngineCron cron = new RuleEngineCron();
+                    cron.setRuleEngineId(ruleEngineId);
+                    cron.setCron(c.getCron().trim());
+                    cron.setCreatedBy(rule.getCreatedBy());
+                    cron.setUpdatedBy(rule.getUpdatedBy());
+                    cron.setTenantId(tenantId);
+                    ruleEngineCronMapper.insert(cron);
+                });
+            }
+
+            ruleEngineDeviceMapper.deleteByRuleEngineId(ruleEngineId);
+            List<DeviceTrigger> deviceTriggers = this.getDeviceTriggers(triggers);
+            if (deviceTriggers != null && !deviceTriggers.isEmpty()) {
+                deviceTriggers.forEach((c) -> {
+                    List<SimpleVO> devices = c.getDevices();
+                    List<Condition> conditionsList = c.getConditions();
+                    devices.forEach((d) -> conditionsList.forEach((conditions) -> {
+                        RuleEngineDevice dev = new RuleEngineDevice();
+                        dev.setRuleEngineId(ruleEngineId);
+                        dev.setDeviceId(d.getId());
+                        dev.setIdentifier(conditions.getIdentifier());
+                        dev.setProductId(c.getProductId());
+                        dev.setCreatedBy(rule.getCreatedBy());
+                        dev.setUpdatedBy(rule.getUpdatedBy());
+                        dev.setTenantId(tenantId);
+                        ruleEngineDeviceMapper.insert(dev);
+                    }));
+                });
+            }
+
+            RuleEngineDTO engineDTO = new RuleEngineDTO();
+            engineDTO.setId(rule.getId());
+            engineDTO.setProjectId(rule.getProjectId());
+            engineDTO.setName(rule.getName());
+            engineDTO.setStatus(rule.getStatus());
+            engineDTO.setDetail(rule.getDetail());
+            ruleEngineDetailService.update(engineDTO);
+        }
+
+    }
+
+    private void verifyJson(RuleEngineDetail ruleEngineDetail) {
+        List<RuleEngineDetail.CommonVO> triggers = ruleEngineDetail.getTriggers();
+        if (CollectionUtils.isNotEmpty(triggers)) {
+            for(RuleEngineDetail.CommonVO vo : triggers) {
+                this.triggerVerify(vo);
+            }
+        }
+
+        List<RuleEngineDetail.CommonVO> constraints = ruleEngineDetail.getConstraints();
+        if (CollectionUtils.isNotEmpty(constraints)) {
+            for(RuleEngineDetail.CommonVO vo : constraints) {
+                this.constraintVerify(vo);
+            }
+        }
+
+        List<RuleEngineDetail.CommonVO> actions = ruleEngineDetail.getActions();
+        if (CollectionUtils.isNotEmpty(actions)) {
+            for(RuleEngineDetail.CommonVO vo : actions) {
+                this.actionVerify(vo);
+            }
+        }
+
+    }
+
+    private void triggerVerify(RuleEngineDetail.CommonVO vo) {
+        String type = vo.getType();
+        String detail = vo.getDetail();
+        if (Util.isBlank(detail)) {
+            throw new BizException("触发条件不能为空");
+        } else {
+            if (TriggerTypeEnum.DEVICE.getType().equals(type)) {
+                DeviceTrigger deviceTrigger = (DeviceTrigger)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, detail, DeviceTrigger.class);
+                if (deviceTrigger.getProductId() == null) {
+                    throw new BizException("触发条件-产品不能为空");
+                }
+
+                if (Util.isBlank(deviceTrigger.getMethod())) {
+                    throw new BizException("触发条件-触发方式不能为空");
+                }
+
+                boolean flag = deviceTrigger.getMethod().equals("consumption");
+                this.conditionsVerify(deviceTrigger.getConditions(), flag, "触发条件-");
+                if (CollectionUtils.isEmpty(deviceTrigger.getDevices())) {
+                    throw new BizException("触发条件-设备不能为空");
+                }
+            } else if (TriggerTypeEnum.CRON.getType().equals(type)) {
+                CronTrigger cronTrigger = (CronTrigger)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, detail, CronTrigger.class);
+                if (Util.isBlank(cronTrigger.getCron())) {
+                    throw new BizException("触发条件-表达式不能为空");
+                }
+
+                if (!CronUtil.isCronMatched(cronTrigger.getCron())) {
+                    throw new BizException("触发条件-表达式格式不正确");
+                }
+            } else if (TriggerTypeEnum.SPACE.getType().equals(type)) {
+                SpaceTrigger spaceTrigger = (SpaceTrigger)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, detail, SpaceTrigger.class);
+                if (Util.isBlank(spaceTrigger.getMethod())) {
+                    throw new BizException("触发条件-触发方式不能为空");
+                }
+
+                if (CollectionUtils.isEmpty(spaceTrigger.getSpaces())) {
+                    throw new BizException("触发条件-空间不能为空");
+                }
+
+                List<Condition> conditions = spaceTrigger.getConditions();
+                if (CollectionUtils.isEmpty(spaceTrigger.getConditions())) {
+                    throw new BizException("触发条件-触发方式不能为空");
+                }
+
+                for(int i = 0; i < conditions.size(); ++i) {
+                    Condition condition = (Condition)conditions.get(i);
+                    if (Util.isBlank(condition.getItemCode())) {
+                        throw new BizException("触发条件-能源分项不能为空");
+                    }
+
+                    TimeRange timeRange = condition.getTimeRange();
+                    if (timeRange.getStart() == null || timeRange.getEnd() == null) {
+                        throw new BizException("触发条件-开始或结束时间不能为空");
+                    }
+
+                    Expression expression = condition.getExpression();
+                    if (expression == null) {
+                        throw new BizException("触发条件-限制条件不能为空");
+                    }
+
+                    if (i > 0 && Util.isBlank(condition.getOperator())) {
+                        throw new BizException("触发条件-逻辑符不能为空");
+                    }
+                }
+            }
+
+        }
+    }
+
+    private void constraintVerify(RuleEngineDetail.CommonVO vo) {
+        String type = vo.getType();
+        String detail = vo.getDetail();
+        if (Util.isBlank(detail)) {
+            throw new BizException("约束条件不能为空");
+        } else {
+            if (type.equals(ConstraintTypeEnum.CRON.getType())) {
+                CronConstraint cronConstraint = (CronConstraint)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, detail, CronConstraint.class);
+                if (Util.isBlank(cronConstraint.getCron())) {
+                    throw new BizException("约束条件-表达式不能为空");
+                }
+
+                if (!CronUtil.isCronMatched(cronConstraint.getCron())) {
+                    throw new BizException("约束条件-表达式格式不正确");
+                }
+            } else if (type.equals(ConstraintTypeEnum.DEVICE.getType())) {
+                DeviceConstraint deviceConstraint = (DeviceConstraint)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, detail, DeviceConstraint.class);
+                if (deviceConstraint.getProductId() == null) {
+                    throw new BizException("约束条件-产品不能为空");
+                }
+
+                if (CollectionUtils.isEmpty(deviceConstraint.getDevices())) {
+                    throw new BizException("约束条件-设备不能为空");
+                }
+
+                this.conditionsVerify(deviceConstraint.getConditions(), false, "约束条件-");
+            }
+
+        }
+    }
+
+    private void actionVerify(RuleEngineDetail.CommonVO vo) {
+        String type = vo.getType();
+        String detail = vo.getDetail();
+        if (Util.isBlank(detail)) {
+            throw new BizException("执行动作不能为空");
+        } else {
+            if (ActionTypeEnum.DEVICE_CONTROL.getType().equals(type)) {
+                DeviceControlAction deviceControlAction = (DeviceControlAction)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, detail, DeviceControlAction.class);
+                if (deviceControlAction.getProductId() == null) {
+                    throw new BizException("执行动作-产品不能为空");
+                }
+
+                List<DeviceControlAction.Functions> functionsList = deviceControlAction.getFunctions();
+                if (CollectionUtils.isEmpty(functionsList)) {
+                    throw new BizException("执行动作-动作不能为空");
+                }
+
+                for(DeviceControlAction.Functions functions : functionsList) {
+                    if (functions.getDelaySeconds() == null) {
+                        throw new BizException("执行动作-延时时间不能为空");
+                    }
+
+                    if (functions.getDelaySeconds() < 1) {
+                        throw new BizException("执行动作-延时时间不能低于1秒");
+                    }
+                }
+
+                List<SimpleVO> devices = deviceControlAction.getDevices();
+                if (CollectionUtils.isEmpty(devices)) {
+                    throw new BizException("执行动作-设备不能为空");
+                }
+            }
+
+        }
+    }
+
+    private void conditionsVerify(List<Condition> conditionList, boolean flag, String name) {
+        if (CollectionUtils.isEmpty(conditionList)) {
+            throw new BizException(name + "功能点及条件不能为空");
+        } else {
+            for(int i = 0; i < conditionList.size(); ++i) {
+                Condition condition = (Condition)conditionList.get(i);
+                if (i > 0 && Util.isBlank(condition.getOperator())) {
+                    throw new BizException(name + "逻辑符不能为空");
+                }
+
+                if (Util.isBlank(condition.getIdentifier())) {
+                    throw new BizException(name + "功能点不能为空");
+                }
+
+                if (flag) {
+                    TimeRange timeRange = condition.getTimeRange();
+                    if (timeRange.getStart() == null || timeRange.getEnd() == null) {
+                        throw new BizException(name + "开始或结束时间不能为空");
+                    }
+                }
+
+                Expression expression = condition.getExpression();
+                if (Util.isBlank(expression.getX()) && Util.isBlank(expression.getY()) && Util.isBlank(expression.getM())) {
+                    throw new BizException(name + "限制条件不能为空");
+                }
+            }
+
+        }
+    }
+
+
+    @Override
+    public RuleEngineConfigDTO getEngineConfig(Long ruleEngineId) {
+        RuleEngine rule = getBaseMapper().selectById(ruleEngineId, SecurityUtils.getTenantId());
+        if (rule == null) {
+            return null;
+        }
+        RuleEngineConfigDTO dto = new RuleEngineConfigDTO();
+        dto.setRule(rule);
+        List<String> cronExprs = ruleEngineCronMapper.selectByRuleEngineId(ruleEngineId).stream()
+                .map(RuleEngineCron::getCron).collect(Collectors.toList());
+        dto.setCronExprs(cronExprs);
+        List<RuleEngineConfigDTO.RuleEngineDeviceItem> devices = new ArrayList<>();
+        for (RuleEngineDevice dev : ruleEngineDeviceMapper.selectByRuleEngineId(ruleEngineId)) {
+            RuleEngineConfigDTO.RuleEngineDeviceItem item = new RuleEngineConfigDTO.RuleEngineDeviceItem();
+            item.setDeviceId(dev.getDeviceId());
+            item.setIdentifier(dev.getIdentifier());
+            item.setProductId(dev.getProductId());
+            devices.add(item);
+        }
+        dto.setDevices(devices);
+        return dto;
+    }
+
+    @Override
+    public void manualTrigger(Long ruleEngineId) {
+        RuleEngine rule = getBaseMapper().selectById(ruleEngineId, SecurityUtils.getTenantId());
+        if (rule == null) {
+            throw new IllegalArgumentException("规则不存在: " + ruleEngineId);
+        }
+
+        if(!ruleEngineDetailService.manualPerformDeviceControl(ruleEngineId)){
+            throw new BizException("触发未成功");
+        }
+        RuleEngineLog logRow = new RuleEngineLog();
+        logRow.setProjectId(rule.getProjectId());
+        logRow.setRuleEngineId(rule.getId());
+        logRow.setRuleEngineName(rule.getName());
+        logRow.setAutoTrigger(0);
+        logRow.setTriggerType("manual");
+        logRow.setAction(null);
+        logRow.setDetail(rule.getDetail());
+        logRow.setContent("手动触发");
+        logRow.setTime(new Date());
+        logRow.setTenantId(rule.getTenantId());
+        ruleEngineLogService.add(logRow);
+    }
+
+    @Override
+    public void triggerByCron(Long ruleEngineId) {
+        RuleEngine rule = getBaseMapper().selectById(ruleEngineId, SecurityUtils.getTenantId());
+        if (rule == null) {
+            return;
+        }
+        RuleEngineLog logRow = new RuleEngineLog();
+        logRow.setProjectId(rule.getProjectId());
+        logRow.setRuleEngineId(rule.getId());
+        logRow.setRuleEngineName(rule.getName());
+        logRow.setAutoTrigger(1);
+        logRow.setTriggerType("cron");
+        logRow.setAction(null);
+        logRow.setDetail(rule.getDetail());
+        logRow.setContent("定时触发");
+        logRow.setTime(new Date());
+        logRow.setTenantId(rule.getTenantId());
+        ruleEngineLogService.add(logRow);
+    }
+
+
+    public List<CronTrigger> getCronTriggers(List<RuleEngineDetail.CommonVO> commonVOList) {
+        if (commonVOList != null && !commonVOList.isEmpty()) {
+            List<CronTrigger> cronTriggerList = new ArrayList();
+            Class<?> clazz = (Class)triggerMap.get(TriggerTypeEnum.CRON.getType());
+            commonVOList.forEach((commonVO) -> {
+                if (TriggerTypeEnum.CRON.getType().equals(commonVO.getType())) {
+                    cronTriggerList.add((CronTrigger)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, commonVO.getDetail(), clazz));
+                }
+
+            });
+            return cronTriggerList;
+        } else {
+            return null;
+        }
+    }
+
+    public List<DeviceTrigger> getDeviceTriggers(List<RuleEngineDetail.CommonVO> commonVOList) {
+        if (commonVOList != null && !commonVOList.isEmpty()) {
+            List<DeviceTrigger> deviceTriggerList = new ArrayList();
+            Class<?> clazz = (Class)triggerMap.get(TriggerTypeEnum.DEVICE.getType());
+            commonVOList.forEach((commonVO) -> {
+                if (TriggerTypeEnum.DEVICE.getType().equals(commonVO.getType())) {
+                    deviceTriggerList.add((DeviceTrigger)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, commonVO.getDetail(), clazz));
+                }
+
+            });
+            return deviceTriggerList;
+        } else {
+            return null;
+        }
+    }
+
+    public List<SpaceTrigger> getSpaceTriggers(List<RuleEngineDetail.CommonVO> commonVOList) {
+        if (commonVOList != null && !commonVOList.isEmpty()) {
+            List<SpaceTrigger> deviceTriggerList = new ArrayList();
+            Class<?> clazz = (Class)triggerMap.get(TriggerTypeEnum.SPACE.getType());
+            commonVOList.forEach((commonVO) -> {
+                if (TriggerTypeEnum.SPACE.getType().equals(commonVO.getType())) {
+                    deviceTriggerList.add((SpaceTrigger)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, commonVO.getDetail(), clazz));
+                }
+
+            });
+            return deviceTriggerList;
+        } else {
+            return null;
+        }
+    }
+
+    public List<CronConstraint> getCronConstraints(List<RuleEngineDetail.CommonVO> commonVOList) {
+        if (commonVOList != null && !commonVOList.isEmpty()) {
+            List<CronConstraint> cronConstraintList = new ArrayList();
+            Class<?> clazz = (Class)constraintMap.get(ConstraintTypeEnum.CRON.getType());
+            commonVOList.forEach((commonVO) -> {
+                if (TriggerTypeEnum.CRON.getType().equals(commonVO.getType())) {
+                    cronConstraintList.add((CronConstraint)JsonUtil.toObject(commonVO.getDetail(), clazz));
+                }
+
+            });
+            return cronConstraintList;
+        } else {
+            return null;
+        }
+    }
+
+    public List<DeviceConstraint> getDeviceConstraints(List<RuleEngineDetail.CommonVO> commonVOList) {
+        if (commonVOList != null && !commonVOList.isEmpty()) {
+            List<DeviceConstraint> deviceConstraintList = new ArrayList();
+            Class<?> clazz = (Class)constraintMap.get(ConstraintTypeEnum.DEVICE.getType());
+            commonVOList.forEach((commonVO) -> {
+                if (ConstraintTypeEnum.DEVICE.getType().equals(commonVO.getType())) {
+                    deviceConstraintList.add((DeviceConstraint)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, commonVO.getDetail(), clazz));
+                }
+
+            });
+            return deviceConstraintList;
+        } else {
+            return null;
+        }
+    }
+
+    public List<RuleEngineAction> getActions(List<RuleEngineDetail.CommonVO> commonVOList) {
+        if (commonVOList != null && !commonVOList.isEmpty()) {
+            List<RuleEngineAction> actionList = new ArrayList();
+            commonVOList.forEach((commonVO) -> {
+                if (ActionTypeEnum.ALARM_EVENT.getType().equals(commonVO.getType())) {
+//                    actionList.add(JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, commonVO.getDetail(), AlarmEventAction.class));
+                } else if (ActionTypeEnum.WORK_ORDER.getType().equals(commonVO.getType())) {
+//                    actionList.add(JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, commonVO.getDetail(), WorkOrderAction.class));
+                } else if (ActionTypeEnum.DEVICE_CONTROL.getType().equals(commonVO.getType())) {
+                    actionList.add(JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, commonVO.getDetail(), DeviceControlAction.class));
+                }
+
+            });
+            return actionList;
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public String getName(Long ruleEngineId) {
+        RuleEngine ruleEngine = getBaseMapper().selectById(ruleEngineId, SecurityUtils.getTenantId());
+        return ruleEngine == null ? "" : ruleEngine.getName();
+    }
+
+    @Override
+    public String getDetail(Long ruleEngineId) {
+        RuleEngine ruleEngine = getBaseMapper().selectById(ruleEngineId, SecurityUtils.getTenantId());
+        return ruleEngine == null ? "" : ruleEngine.getDetail();
+    }
+}

+ 455 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/subscribe/TriggerDeviceUtil.java

@@ -0,0 +1,455 @@
+package com.usky.rule.subscribe;
+
+import com.usky.rule.constant.DateTimeConstants;
+import com.usky.rule.util.DateTimeUtil;
+import com.usky.rule.util.JsonUtil;
+import com.usky.rule.domain.RuleEngine;
+import com.usky.rule.enums.ConstraintTypeEnum;
+import com.usky.rule.enums.TriggerTypeEnum;
+import com.usky.rule.enums.TriggerValueTypeEnum;
+import com.usky.rule.vo.RuleEngineDeviceVO;
+import com.usky.rule.vo.Condition;
+import com.usky.rule.vo.Expression;
+import com.usky.rule.vo.RuleEngineDetail;
+import com.usky.rule.vo.action.RuleEngineAction;
+import com.usky.rule.vo.constraint.DeviceConstraint;
+import com.usky.rule.vo.log.BaseLog;
+import com.usky.rule.vo.log.DeviceTriggerLog;
+import com.usky.rule.vo.log.RuleEngineDetailLog;
+import com.usky.rule.vo.trigger.DeviceTrigger;
+import com.usky.rule.vo.visualization.SimpleVO;
+import com.usky.rule.cache.DeviceTriggerIncludeMinuteCache;
+import com.usky.rule.util.RuleEngineCallBack;
+import com.usky.rule.util.RuleEngineUtil;
+import com.usky.rule.vo.ConditionExpression;
+import com.usky.rule.vo.DeviceDataVO;
+//import com.leo.service.device.DeviceFunctionService;
+//import com.leo.service.device.DeviceService;
+import com.usky.rule.service.RuleEngineDeviceService;
+import com.usky.rule.service.RuleEngineService;
+import java.math.BigDecimal;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import com.usky.rule.vo.log.BaseLog;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TriggerDeviceUtil {
+    private RuleEngineDeviceService ruleEngineDeviceService;
+    private RuleEngineService ruleEngineService;
+//    private DeviceFunctionService deviceFunctionService;
+    private RuleEngineUtil ruleEngineUtil;
+    private DeviceTriggerIncludeMinuteCache historyRecordCache;
+//    private DeviceService deviceService;
+    private static final Integer maxNumberOfReminders = 1;
+    private static final ExpressionParser parser = new SpelExpressionParser();
+
+    public void processMessage(String message) {
+        LocalDateTime now = LocalDateTime.now();
+        DeviceDataVO deviceDataVO = (DeviceDataVO)JsonUtil.toObject(JsonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER, message, DeviceDataVO.class);
+        String deviceId = deviceDataVO.getDeviceId();
+        LocalDateTime currDataTime = DateTimeUtil.parse(deviceDataVO.getAcqTime(), DateTimeConstants.SIMPLE_DATE_TIME_FORMATTER);
+        Map<String, String> valueMap = deviceDataVO.getFunctions();
+        List<RuleEngineDeviceVO> ruleEngineDeviceVOList = this.ruleEngineDeviceService.getByDeviceId(deviceId);
+        if (!ruleEngineDeviceVOList.isEmpty()) {
+            ruleEngineDeviceVOList.forEach((ruleEngineDeviceVO) -> {
+                Long ruleEngineId = ruleEngineDeviceVO.getRuleEngineId();
+                Long productId = ruleEngineDeviceVO.getProductId();
+                RuleEngine ruleEngine = (RuleEngine)this.ruleEngineService.getById(ruleEngineId);
+                if (ruleEngine != null && ruleEngine.getStatus() != 0) {
+                    if (!StringUtils.isBlank(ruleEngine.getDetail())) {
+                        RuleEngineDetail ruleEngineDetail = (RuleEngineDetail)JsonUtil.toObject(ruleEngine.getDetail(), RuleEngineDetail.class);
+                        List<RuleEngineAction> actions = this.ruleEngineService.getActions(ruleEngineDetail.getActions());
+                        if (!actions.isEmpty()) {
+                            List<DeviceTrigger> deviceTriggers = this.ruleEngineService.getDeviceTriggers(ruleEngineDetail.getTriggers());
+                            if (deviceTriggers != null) {
+                                deviceTriggers = (List)deviceTriggers.stream().filter((deviceTrigger) -> deviceTrigger.getMethod().equals("acq")).filter((deviceTrigger) -> deviceTrigger.getProductId().equals(productId)).filter((deviceTrigger) -> deviceTrigger.getDevices().stream().anyMatch((device) -> device.getId().equals(deviceId))).collect(Collectors.toList());
+                                if (!deviceTriggers.isEmpty()) {
+                                    RuleEngineDetailLog ruleEngineDetailLog = new RuleEngineDetailLog();
+                                    List<Condition> meetTriggerConditionList = new ArrayList();
+                                    Map<String, String> meetMinuteExpressionMap = new HashMap();
+                                    boolean triggerAction = this.meetDeviceAcqTriggerAction(ruleEngineId, deviceId, currDataTime, valueMap, deviceTriggers, meetMinuteExpressionMap, meetTriggerConditionList);
+                                    if (triggerAction) {
+                                        this.setTriggerLog(now, ruleEngineDetailLog, deviceId, TriggerValueTypeEnum.ACQ.getValue(), TriggerTypeEnum.DEVICE, meetTriggerConditionList, valueMap);
+                                        List<DeviceConstraint> deviceConstraints = this.ruleEngineService.getDeviceConstraints(ruleEngineDetail.getConstraints());
+//                                        boolean constraintAction = this.meetConstraintAction(deviceConstraints, ruleEngineDetailLog);
+//                                        if (constraintAction) {
+//                                            this.ruleEngineUtil.performMultipleDevicesControl(ruleEngineId, true, TriggerTypeEnum.DEVICE.getType(), ruleEngine.getProjectId(), ruleEngine.getSpaceId(), actions, ruleEngineDetailLog);
+//                                            this.clearMeetConditionCache(ruleEngineId, deviceId, meetMinuteExpressionMap);
+//                                        }
+                                    }
+
+                                }
+                            }
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    private void clearMeetConditionCache(Long ruleEngineId, String deviceId, Map<String, String> meetMinuteExpressionMap) {
+        if (meetMinuteExpressionMap != null) {
+//            meetMinuteExpressionMap.forEach((identifier, exp) -> this.historyRecordCache.removeCondition(ruleEngineId, deviceId, identifier, exp));
+        }
+
+    }
+
+    public void setTriggerLog(LocalDateTime now, RuleEngineDetailLog ruleEngineDetailLog, String deviceId, String method, TriggerTypeEnum triggerTypeEnum, List<Condition> meetTriggerConditionList, Map<String, String> valueMap) {
+        DeviceTriggerLog deviceTriggerLog = new DeviceTriggerLog();
+        deviceTriggerLog.setId(deviceId);
+//        deviceTriggerLog.setName(this.deviceService.getName(deviceId));
+        deviceTriggerLog.setMethod(method);
+        deviceTriggerLog.setTime(DateTimeUtil.format(now));
+        deviceTriggerLog.setConditions(meetTriggerConditionList);
+
+        for(Condition condition : meetTriggerConditionList) {
+//            condition.setName(this.deviceFunctionService.getName(deviceId, condition.getIdentifier()));
+            condition.setValue((String)valueMap.get(condition.getIdentifier()));
+        }
+
+        BaseLog baseLog = new BaseLog();
+        baseLog.setType(triggerTypeEnum.getType());
+        baseLog.setDetail(deviceTriggerLog);
+        List<BaseLog> baseLogs = new ArrayList();
+        baseLogs.add(baseLog);
+        ruleEngineDetailLog.setTriggers(baseLogs);
+    }
+
+    private boolean meetDeviceAcqTriggerAction(Long ruleEngineId, String deviceId, LocalDateTime currDataTime, Map<String, String> valueMap, List<DeviceTrigger> deviceTriggers, Map<String, String> meetMinuteExpressionMap, List<Condition> meetTriggerConditionList) {
+        StringBuilder booleanExpression = new StringBuilder();
+
+        for(int j = 0; j < deviceTriggers.size(); ++j) {
+            List<Condition> conditionList = ((DeviceTrigger)deviceTriggers.get(j)).getConditions();
+            if (j != 0) {
+                booleanExpression.append("||");
+            }
+
+            booleanExpression.append("(");
+
+            for(int i = 0; i < conditionList.size(); ++i) {
+                Condition cond = (Condition)conditionList.get(i);
+                String condition = cond.getCondition();
+                String identifier = cond.getIdentifier();
+                if (valueMap.get(identifier) == null) {
+                    booleanExpression.append(false);
+                } else {
+                    BigDecimal value = new BigDecimal((String)valueMap.get(identifier));
+                    Expression expression = cond.getExpression();
+                    BigDecimal x = new BigDecimal(expression.getX());
+                    BigDecimal y = null;
+                    if (expression.getY() != null) {
+                        y = new BigDecimal(expression.getY());
+                    }
+
+                    Integer m = null;
+                    if (expression.getM() != null) {
+                        m = Integer.parseInt(expression.getM());
+                    }
+
+                    boolean meetCondition = false;
+                    String express = null;
+                    switch (condition) {
+                        case "above_minute":
+                            express = ">" + x;
+                            if (value.compareTo(x) > 0) {
+                                meetCondition = this.isMeetMinuteCondition(ruleEngineId, deviceId, identifier, express, m, currDataTime);
+                            } else {
+                                this.historyRecordCache.removeCondition(ruleEngineId, deviceId, identifier, express);
+                            }
+                            break;
+                        case "below_minute":
+                            express = "<" + x;
+                            if (value.compareTo(x) < 0) {
+                                meetCondition = this.isMeetMinuteCondition(ruleEngineId, deviceId, identifier, express, m, currDataTime);
+                            } else {
+                                this.historyRecordCache.removeCondition(ruleEngineId, deviceId, identifier, express);
+                            }
+                            break;
+                        case "equal_minute":
+                            express = "=" + x;
+                            if (value.compareTo(x) == 0) {
+                                meetCondition = this.isMeetMinuteCondition(ruleEngineId, deviceId, identifier, express, m, currDataTime);
+                            } else {
+                                this.historyRecordCache.removeCondition(ruleEngineId, deviceId, identifier, express);
+                            }
+                            break;
+                        case "not_equal_minute":
+                            express = "!=" + x;
+                            if (value.compareTo(x) == 0) {
+                                meetCondition = this.isMeetMinuteCondition(ruleEngineId, deviceId, identifier, express, m, currDataTime);
+                            } else {
+                                this.historyRecordCache.removeCondition(ruleEngineId, deviceId, identifier, express);
+                            }
+                            break;
+                        case "between_minute":
+                            express = ">=" + x + "&&<=" + y;
+                            if (isBetweenValue(value, x, y)) {
+                                meetCondition = this.isMeetMinuteCondition(ruleEngineId, deviceId, identifier, express, m, currDataTime);
+                            } else {
+                                this.historyRecordCache.removeCondition(ruleEngineId, deviceId, identifier, express);
+                            }
+                            break;
+                        case "above":
+                            if (value.compareTo(x) > 0) {
+                                meetCondition = true;
+                            }
+                            break;
+                        case "below":
+                            if (value.compareTo(x) < 0) {
+                                meetCondition = true;
+                            }
+                            break;
+                        case "equal":
+                            if (value.compareTo(x) == 0) {
+                                meetCondition = true;
+                            }
+                            break;
+                        case "not_equal":
+                            if (value.compareTo(x) != 0) {
+                                meetCondition = true;
+                            }
+                            break;
+                        case "between":
+                            if (isBetweenValue(value, x, y)) {
+                                meetCondition = true;
+                            }
+                    }
+
+                    if (meetCondition) {
+                        meetTriggerConditionList.add(cond);
+                        meetMinuteExpressionMap.put(identifier, express);
+                    }
+
+                    String operator = cond.getOperator();
+                    if (i != 0) {
+                        if (checkOperator(operator)) {
+                            throw new RuntimeException("operator的值必须是 && or ||");
+                        }
+
+                        booleanExpression.append(operator);
+                    }
+
+                    booleanExpression.append(meetCondition);
+                }
+            }
+
+            booleanExpression.append(")");
+        }
+
+        return getBooleanExpressionValue(booleanExpression.toString());
+    }
+
+    private void setMinuteCache(Long ruleEngineId, String deviceId, LocalDateTime currDataTime, String identifier, Integer m, String express) {
+        ConditionExpression conditionExpression = getConditionExpression(express, m, currDataTime);
+        this.historyRecordCache.setCondition(ruleEngineId, deviceId, identifier, conditionExpression);
+    }
+
+    public static RuleEngineCallBack<List<ConditionExpression>, Void> getCallBackFunc(LocalDateTime currDataTime) {
+        return (conditions) -> {
+            if (!conditions.isEmpty()) {
+                conditions.forEach((conditionExpression) -> conditionExpression.setLastMeetConditionTime(currDataTime));
+            }
+
+            return null;
+        };
+    }
+
+    public boolean meetConstraintAction(List<DeviceConstraint> deviceConstraints, RuleEngineDetailLog ruleEngineDetailLog) {
+        List<DeviceTriggerLog> deviceTriggerLogs = new ArrayList();
+        boolean constraintAction = true;
+        if (deviceConstraints != null) {
+            label49:
+            for(DeviceConstraint deviceConstraint : deviceConstraints) {
+                String deviceId = ((SimpleVO)deviceConstraint.getDevices().get(0)).getId();
+                DeviceTriggerLog deviceTriggerLog = new DeviceTriggerLog();
+                List<Condition> meetConstraintConditionList = new ArrayList();
+                deviceTriggerLog.setId(deviceId);
+                deviceTriggerLog.setMethod("acq");
+//                deviceTriggerLog.setName(this.deviceService.getName(deviceId));
+                deviceTriggerLog.setConditions(meetConstraintConditionList);
+                deviceTriggerLogs.add(deviceTriggerLog);
+                List<Condition> identifierConditions = deviceConstraint.getConditions();
+                StringBuilder boolConstraintExp = new StringBuilder();
+
+                for(int i = 0; i < identifierConditions.size(); ++i) {
+                    Condition identifierCondition = (Condition)identifierConditions.get(i);
+                    String operator = identifierCondition.getOperator();
+                    if (i != 0) {
+                        if (checkOperator(operator)) {
+                            throw new RuntimeException("operator的值必须是 && or ||");
+                        }
+
+                        boolConstraintExp.append(operator);
+                        if (checkIsFalse(boolConstraintExp.toString())) {
+                            constraintAction = false;
+                            break label49;
+                        }
+                    }
+
+                    String deviceIdentifier = identifierCondition.getIdentifier();
+                    BigDecimal currValue = this.getCurrDeviceIdentifierValue(deviceId, deviceIdentifier);
+                    if (currValue == null) {
+                        boolConstraintExp.append(false);
+                    } else {
+                        String condition = identifierCondition.getCondition();
+                        Expression expression = identifierCondition.getExpression();
+                        boolean meetCondition = isMeetCondition(currValue, condition, expression.getX(), expression.getY());
+                        if (meetCondition) {
+                            meetConstraintConditionList.add(identifierCondition);
+                        }
+
+                        boolConstraintExp.append(meetCondition);
+                    }
+                }
+
+                constraintAction = getBooleanExpressionValue(boolConstraintExp.toString());
+                if (!constraintAction) {
+                    break;
+                }
+            }
+        }
+
+        if (constraintAction && !deviceTriggerLogs.isEmpty()) {
+            List<BaseLog> baseLogs = new ArrayList();
+            deviceTriggerLogs.forEach((deviceConstraintLog) -> {
+                BaseLog baseLog = new BaseLog();
+                baseLog.setType(ConstraintTypeEnum.DEVICE.getType());
+                baseLog.setDetail(deviceConstraintLog);
+                baseLogs.add(baseLog);
+            });
+            ruleEngineDetailLog.setConstraints(baseLogs);
+        }
+
+        return constraintAction;
+    }
+
+    public static boolean isMeetCondition(BigDecimal currValue, String condition, String X, String Y) {
+        BigDecimal x = new BigDecimal(X);
+        BigDecimal y = null;
+        if (Y != null) {
+            y = new BigDecimal(Y);
+        }
+
+        boolean meetCondition = false;
+        switch (condition) {
+            case "above":
+                if (currValue.compareTo(x) > 0) {
+                    meetCondition = true;
+                }
+                break;
+            case "below":
+                if (currValue.compareTo(x) < 0) {
+                    meetCondition = true;
+                }
+                break;
+            case "equal":
+                if (currValue.compareTo(x) == 0) {
+                    meetCondition = true;
+                }
+                break;
+            case "not_equal":
+                if (currValue.compareTo(x) != 0) {
+                    meetCondition = true;
+                }
+                break;
+            case "between":
+                if (isBetweenValue(currValue, x, y)) {
+                    meetCondition = true;
+                }
+        }
+
+        return meetCondition;
+    }
+
+    public static boolean isMeetConsumptionCondition(BigDecimal currValue, String condition, String X) {
+        BigDecimal x = new BigDecimal(X);
+        boolean meetCondition = false;
+        switch (condition) {
+            case "above":
+                if (currValue.compareTo(x) > 0) {
+                    meetCondition = true;
+                }
+                break;
+            case "above_equal":
+                if (currValue.compareTo(x) >= 0) {
+                    meetCondition = true;
+                }
+                break;
+            case "below":
+                if (currValue.compareTo(x) < 0) {
+                    meetCondition = true;
+                }
+                break;
+            case "below_equal":
+                if (currValue.compareTo(x) <= 0) {
+                    meetCondition = true;
+                }
+        }
+
+        return meetCondition;
+    }
+
+    public static boolean checkIsFalse(String boolConstraintExp) {
+        return !getBooleanExpressionValue(boolConstraintExp + "true");
+    }
+
+    public BigDecimal getCurrDeviceIdentifierValue(String id, String deviceIdentifier) {
+//        String value = this.deviceFunctionService.getCurrDeviceIdentifierValue(id, deviceIdentifier);
+//        return (BigDecimal)Optional.ofNullable(value).map(BigDecimal::new).orElse((Object)null);
+        return null;
+    }
+
+    private static boolean isBetweenValue(BigDecimal value, BigDecimal x, BigDecimal y) {
+        return value.compareTo(x) >= 0 && value.compareTo(y) <= 0;
+    }
+
+    public static boolean checkOperator(String operator) {
+        return !operator.matches("(\\|\\||&&)");
+    }
+
+    public static boolean getBooleanExpressionValue(String booleanExpression) {
+        return StringUtils.isBlank(booleanExpression) ? false : Boolean.TRUE.equals(parser.parseExpression(booleanExpression).getValue(Boolean.class));
+    }
+
+    public Boolean isMeetMinuteCondition(Long ruleEngineId, String deviceId, String identifier, String expression, Integer overMinutes, LocalDateTime currDataTime) {
+        ConditionExpression condition = this.historyRecordCache.getConditions(ruleEngineId, deviceId, identifier, expression);
+        if (condition == null) {
+            this.setMinuteCache(ruleEngineId, deviceId, currDataTime, identifier, overMinutes, expression);
+            return false;
+        } else {
+            LocalDateTime lastMeetConditionTime = condition.getLastMeetConditionTime();
+            long actualOverMinutes = Duration.between(lastMeetConditionTime, currDataTime).toMinutes();
+            Integer actionNumbers = (Integer)Optional.ofNullable(condition.getNumberOfReminders()).orElse(0);
+            return actionNumbers < maxNumberOfReminders && actualOverMinutes > (long)overMinutes;
+        }
+    }
+
+    private static ConditionExpression getConditionExpression(String expression, Integer overMinutes, LocalDateTime currDataTime) {
+        ConditionExpression conditionExpression = new ConditionExpression();
+        conditionExpression.setExpression(expression);
+        conditionExpression.setOverMinutes(overMinutes);
+        conditionExpression.setNumberOfReminders(0);
+        conditionExpression.setLastMeetConditionTime(currDataTime);
+        return conditionExpression;
+    }
+
+//    public TriggerDeviceUtil(final RuleEngineDeviceService ruleEngineDeviceService, final RuleEngineService ruleEngineService, final DeviceFunctionService deviceFunctionService, final RuleEngineUtil ruleEngineUtil, final DeviceTriggerIncludeMinuteCache historyRecordCache, final DeviceService deviceService) {
+//        this.ruleEngineDeviceService = ruleEngineDeviceService;
+//        this.ruleEngineService = ruleEngineService;
+//        this.deviceFunctionService = deviceFunctionService;
+//        this.ruleEngineUtil = ruleEngineUtil;
+//        this.historyRecordCache = historyRecordCache;
+//        this.deviceService = deviceService;
+//    }
+}

+ 37 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/util/CronUtil.java

@@ -0,0 +1,37 @@
+package com.usky.rule.util;
+
+import com.usky.rule.exception.BizException;
+import java.text.ParseException;
+import java.util.Date;
+import org.quartz.CronExpression;
+
+public class CronUtil {
+    public CronUtil() {
+    }
+
+    public static boolean isCronExpired(String cronExpression) {
+        CronExpression cron;
+        try {
+            cron = new CronExpression(cronExpression);
+        } catch (ParseException var4) {
+            throw new BizException(cronExpression + "不是一个正确的cron表达式");
+        }
+
+        Date now = new Date();
+        Date next = cron.getNextValidTimeAfter(now);
+        return next == null || next.before(now);
+    }
+
+    public static boolean isCronMatched(String cronExpression) {
+        CronExpression cron;
+        try {
+            cron = new CronExpression(cronExpression);
+        } catch (ParseException var4) {
+            throw new BizException(cronExpression + "不是一个正确的cron表达式");
+        }
+
+        Date now = new Date();
+        Date next = cron.getNextValidTimeAfter(now);
+        return next != null;
+    }
+}

+ 77 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/util/DateTimeUtil.java

@@ -0,0 +1,77 @@
+package com.usky.rule.util;
+
+import com.usky.rule.constant.DateTimeConstants;
+import com.usky.rule.constant.RegExpConstants;
+import feign.Util;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+public class DateTimeUtil {
+    private DateTimeUtil() {
+    }
+
+    public static LocalDateTime dateToLocalDateTime(Date date) {
+        return date != null ? LocalDateTime.ofInstant(date.toInstant(), getZoneId()) : null;
+    }
+
+    public static Date localDateTimeToDate(LocalDateTime dateTime) {
+        return dateTime != null ? Date.from(dateTime.atZone(getZoneId()).toInstant()) : null;
+    }
+
+    public static LocalDateTime timestampToLocalDateTime(Timestamp timestamp) {
+        return timestamp != null ? LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp.getTime()), getZoneId()) : null;
+    }
+
+    public static String format(LocalDateTime dateTime) {
+        return dateTime != null ? dateTime.format(DateTimeConstants.DATE_TIME_FORMATTER) : null;
+    }
+
+    public static String format(LocalDateTime dateTime, DateTimeFormatter formatter) {
+        return dateTime != null ? dateTime.format(formatter) : null;
+    }
+
+    public static LocalDateTime parse(String dateTime) {
+        return parse(dateTime, DateTimeConstants.DATE_TIME_FORMATTER);
+    }
+
+    public static LocalDateTime parse(String dateTime, DateTimeFormatter formatter) {
+        return Util.isNotBlank(dateTime) ? LocalDateTime.parse(dateTime, formatter) : null;
+    }
+
+    public static long toEpochMilli(LocalDateTime dateTime) {
+        if (dateTime == null) {
+            throw new IllegalArgumentException("dateTime is null");
+        } else {
+            return dateTime.atZone(getZoneId()).toInstant().toEpochMilli();
+        }
+    }
+
+    public static ZoneId getZoneId() {
+        return ZoneOffset.of("+8");
+    }
+
+    public static boolean isValidDateTime(String dateTime) {
+        int dataTimeStringLength = 14;
+        return dateTime.length() != 14 ? false : RegExpConstants.SIMPLE_DATE_TIME_PATTERN.matcher(dateTime).matches();
+    }
+
+    public static String secondToTime(Long second) {
+        if (second != null && second != 0L) {
+            long days = second / 86400L;
+            second = second % 86400L;
+            long hours = second / 3600L;
+            second = second % 3600L;
+            long minutes = second / 60L;
+            second = second % 60L;
+            return days > 0L ? String.format("%d天%d小时%d分%d秒", days, hours, minutes, second) : String.format("%d小时%d分%d秒", hours, minutes, second);
+        } else {
+            return "";
+        }
+    }
+}

+ 26 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/util/JsonToStringDeserializer.java

@@ -0,0 +1,26 @@
+package com.usky.rule.util;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.usky.rule.exception.BizException;
+import java.io.IOException;
+
+public class JsonToStringDeserializer extends JsonDeserializer<String> {
+    public JsonToStringDeserializer() {
+    }
+
+    public String deserialize(JsonParser jp, DeserializationContext context) throws IOException {
+        ObjectMapper mapper = (ObjectMapper)jp.getCodec();
+        JsonNode node = (JsonNode)mapper.readTree(jp);
+
+        try {
+            return mapper.writeValueAsString(node);
+        } catch (JsonProcessingException var6) {
+            throw new BizException("格式有误");
+        }
+    }
+}

+ 81 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/util/JsonUtil.java

@@ -0,0 +1,81 @@
+package com.usky.rule.util;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+public class JsonUtil {
+    public static final ObjectMapper JSON_MAPPER = (new ObjectMapper()).findAndRegisterModules();
+    public static final ObjectMapper IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER;
+
+    private JsonUtil() {
+    }
+
+    public static String toJson(Object object) {
+        try {
+            return JSON_MAPPER.writeValueAsString(object);
+        } catch (JsonProcessingException e) {
+            throw new IllegalArgumentException(String.format("将对象转换为JSON出错,%s", e.getMessage()), e);
+        }
+    }
+
+    public static String toJson(ObjectMapper mapper, Object object) {
+        try {
+            return mapper.writeValueAsString(object);
+        } catch (JsonProcessingException e) {
+            throw new IllegalArgumentException(String.format("将对象转换为JSON出错,%s", e.getMessage()), e);
+        }
+    }
+
+    public static <T> T toObject(String content, Class<T> type) {
+        return (T)toObject(JSON_MAPPER, content, type);
+    }
+
+    public static <T> T toObject(Map<String, Object> map, Class<T> type) {
+        try {
+            return (T)JSON_MAPPER.convertValue(map, type);
+        } catch (Exception e) {
+            throw new IllegalArgumentException(String.format("将JSON转换为对象出错,%s", e.getMessage()), e);
+        }
+    }
+
+    public static <T> T toObject(ObjectMapper mapper, String content, Class<T> type) {
+        try {
+            return (T)mapper.readValue(content, type);
+        } catch (IOException e) {
+            throw new IllegalArgumentException(String.format("将JSON转换为对象出错,%s", e.getMessage()), e);
+        }
+    }
+
+    public static <T> T parse(String content, TypeReference<T> typeReference) {
+        try {
+            return (T)JSON_MAPPER.readValue(content, typeReference);
+        } catch (IOException e) {
+            throw new IllegalArgumentException(String.format("将JSON转换为对象出错,%s", e.getMessage()), e);
+        }
+    }
+
+    public static <T> List<T> parseJsonArray(String content, Class<T> type) {
+        try {
+            return (List)JSON_MAPPER.readValue(content, JSON_MAPPER.getTypeFactory().constructCollectionType(List.class, type));
+        } catch (JsonProcessingException e) {
+            throw new IllegalArgumentException(String.format("将对象转换为JSON数组出错,%s", e.getMessage()), e);
+        }
+    }
+
+    public static byte[] toByteArray(Object object) {
+        try {
+            return JSON_MAPPER.writeValueAsBytes(object);
+        } catch (JsonProcessingException e) {
+            throw new IllegalArgumentException(String.format("将对象转换为JSON出错,%s", e.getMessage()), e);
+        }
+    }
+
+    static {
+        IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER = (new ObjectMapper()).disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).findAndRegisterModules();
+    }
+}

+ 6 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/util/RuleEngineCallBack.java

@@ -0,0 +1,6 @@
+package com.usky.rule.util;
+
+@FunctionalInterface
+public interface RuleEngineCallBack<P, R> {
+    R call(P param);
+}

+ 14 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/util/RuleEngineUtil.java

@@ -0,0 +1,14 @@
+package com.usky.rule.util;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class RuleEngineUtil {
+    public static String getJobGroup(Long ruleEngineId) {
+        return "trigger-" + ruleEngineId;
+    }
+
+    public static String getTriggerCronJobName(Integer index) {
+        return "job-" + index;
+    }
+}

+ 245 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/Condition.java

@@ -0,0 +1,245 @@
+package com.usky.rule.vo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Condition {
+    @JsonProperty("itemCode")
+    private String itemCode;
+    @JsonProperty("itemName")
+    private String itemName;
+    @JsonProperty("identifier")
+    private String identifier;
+    @JsonProperty("name")
+    private String name;
+    @JsonProperty("value")
+    private String value;
+    @JsonProperty("condition")
+    private String condition;
+    @JsonProperty("timeRange")
+    private TimeRange timeRange;
+    @JsonProperty("expression")
+    private Expression expression;
+    @JsonProperty("operator")
+    private String operator;
+
+    public Condition() {
+    }
+
+    public String getItemCode() {
+        return this.itemCode;
+    }
+
+    public String getItemName() {
+        return this.itemName;
+    }
+
+    public String getIdentifier() {
+        return this.identifier;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getValue() {
+        return this.value;
+    }
+
+    public String getCondition() {
+        return this.condition;
+    }
+
+    public TimeRange getTimeRange() {
+        return this.timeRange;
+    }
+
+    public Expression getExpression() {
+        return this.expression;
+    }
+
+    public String getOperator() {
+        return this.operator;
+    }
+
+    @JsonProperty("itemCode")
+    public void setItemCode(final String itemCode) {
+        this.itemCode = itemCode;
+    }
+
+    @JsonProperty("itemName")
+    public void setItemName(final String itemName) {
+        this.itemName = itemName;
+    }
+
+    @JsonProperty("identifier")
+    public void setIdentifier(final String identifier) {
+        this.identifier = identifier;
+    }
+
+    @JsonProperty("name")
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    @JsonProperty("value")
+    public void setValue(final String value) {
+        this.value = value;
+    }
+
+    @JsonProperty("condition")
+    public void setCondition(final String condition) {
+        this.condition = condition;
+    }
+
+    @JsonProperty("timeRange")
+    public void setTimeRange(final TimeRange timeRange) {
+        this.timeRange = timeRange;
+    }
+
+    @JsonProperty("expression")
+    public void setExpression(final Expression expression) {
+        this.expression = expression;
+    }
+
+    @JsonProperty("operator")
+    public void setOperator(final String operator) {
+        this.operator = operator;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof Condition)) {
+            return false;
+        } else {
+            Condition other = (Condition)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$itemCode = this.getItemCode();
+                Object other$itemCode = other.getItemCode();
+                if (this$itemCode == null) {
+                    if (other$itemCode != null) {
+                        return false;
+                    }
+                } else if (!this$itemCode.equals(other$itemCode)) {
+                    return false;
+                }
+
+                Object this$itemName = this.getItemName();
+                Object other$itemName = other.getItemName();
+                if (this$itemName == null) {
+                    if (other$itemName != null) {
+                        return false;
+                    }
+                } else if (!this$itemName.equals(other$itemName)) {
+                    return false;
+                }
+
+                Object this$identifier = this.getIdentifier();
+                Object other$identifier = other.getIdentifier();
+                if (this$identifier == null) {
+                    if (other$identifier != null) {
+                        return false;
+                    }
+                } else if (!this$identifier.equals(other$identifier)) {
+                    return false;
+                }
+
+                Object this$name = this.getName();
+                Object other$name = other.getName();
+                if (this$name == null) {
+                    if (other$name != null) {
+                        return false;
+                    }
+                } else if (!this$name.equals(other$name)) {
+                    return false;
+                }
+
+                Object this$value = this.getValue();
+                Object other$value = other.getValue();
+                if (this$value == null) {
+                    if (other$value != null) {
+                        return false;
+                    }
+                } else if (!this$value.equals(other$value)) {
+                    return false;
+                }
+
+                Object this$condition = this.getCondition();
+                Object other$condition = other.getCondition();
+                if (this$condition == null) {
+                    if (other$condition != null) {
+                        return false;
+                    }
+                } else if (!this$condition.equals(other$condition)) {
+                    return false;
+                }
+
+                Object this$timeRange = this.getTimeRange();
+                Object other$timeRange = other.getTimeRange();
+                if (this$timeRange == null) {
+                    if (other$timeRange != null) {
+                        return false;
+                    }
+                } else if (!this$timeRange.equals(other$timeRange)) {
+                    return false;
+                }
+
+                Object this$expression = this.getExpression();
+                Object other$expression = other.getExpression();
+                if (this$expression == null) {
+                    if (other$expression != null) {
+                        return false;
+                    }
+                } else if (!this$expression.equals(other$expression)) {
+                    return false;
+                }
+
+                Object this$operator = this.getOperator();
+                Object other$operator = other.getOperator();
+                if (this$operator == null) {
+                    if (other$operator != null) {
+                        return false;
+                    }
+                } else if (!this$operator.equals(other$operator)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof Condition;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $itemCode = this.getItemCode();
+        result = result * 59 + ($itemCode == null ? 43 : $itemCode.hashCode());
+        Object $itemName = this.getItemName();
+        result = result * 59 + ($itemName == null ? 43 : $itemName.hashCode());
+        Object $identifier = this.getIdentifier();
+        result = result * 59 + ($identifier == null ? 43 : $identifier.hashCode());
+        Object $name = this.getName();
+        result = result * 59 + ($name == null ? 43 : $name.hashCode());
+        Object $value = this.getValue();
+        result = result * 59 + ($value == null ? 43 : $value.hashCode());
+        Object $condition = this.getCondition();
+        result = result * 59 + ($condition == null ? 43 : $condition.hashCode());
+        Object $timeRange = this.getTimeRange();
+        result = result * 59 + ($timeRange == null ? 43 : $timeRange.hashCode());
+        Object $expression = this.getExpression();
+        result = result * 59 + ($expression == null ? 43 : $expression.hashCode());
+        Object $operator = this.getOperator();
+        result = result * 59 + ($operator == null ? 43 : $operator.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "Condition(itemCode=" + this.getItemCode() + ", itemName=" + this.getItemName() + ", identifier=" + this.getIdentifier() + ", name=" + this.getName() + ", value=" + this.getValue() + ", condition=" + this.getCondition() + ", timeRange=" + this.getTimeRange() + ", expression=" + this.getExpression() + ", operator=" + this.getOperator() + ")";
+    }
+}

+ 122 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/ConditionExpression.java

@@ -0,0 +1,122 @@
+package com.usky.rule.vo;
+
+import java.time.LocalDateTime;
+
+public class ConditionExpression {
+    String expression;
+    Integer overMinutes;
+    LocalDateTime lastMeetConditionTime;
+    Integer numberOfReminders;
+
+    public ConditionExpression() {
+    }
+
+    public String getExpression() {
+        return this.expression;
+    }
+
+    public Integer getOverMinutes() {
+        return this.overMinutes;
+    }
+
+    public LocalDateTime getLastMeetConditionTime() {
+        return this.lastMeetConditionTime;
+    }
+
+    public Integer getNumberOfReminders() {
+        return this.numberOfReminders;
+    }
+
+    public void setExpression(final String expression) {
+        this.expression = expression;
+    }
+
+    public void setOverMinutes(final Integer overMinutes) {
+        this.overMinutes = overMinutes;
+    }
+
+    public void setLastMeetConditionTime(final LocalDateTime lastMeetConditionTime) {
+        this.lastMeetConditionTime = lastMeetConditionTime;
+    }
+
+    public void setNumberOfReminders(final Integer numberOfReminders) {
+        this.numberOfReminders = numberOfReminders;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof ConditionExpression)) {
+            return false;
+        } else {
+            ConditionExpression other = (ConditionExpression)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$overMinutes = this.getOverMinutes();
+                Object other$overMinutes = other.getOverMinutes();
+                if (this$overMinutes == null) {
+                    if (other$overMinutes != null) {
+                        return false;
+                    }
+                } else if (!this$overMinutes.equals(other$overMinutes)) {
+                    return false;
+                }
+
+                Object this$numberOfReminders = this.getNumberOfReminders();
+                Object other$numberOfReminders = other.getNumberOfReminders();
+                if (this$numberOfReminders == null) {
+                    if (other$numberOfReminders != null) {
+                        return false;
+                    }
+                } else if (!this$numberOfReminders.equals(other$numberOfReminders)) {
+                    return false;
+                }
+
+                Object this$expression = this.getExpression();
+                Object other$expression = other.getExpression();
+                if (this$expression == null) {
+                    if (other$expression != null) {
+                        return false;
+                    }
+                } else if (!this$expression.equals(other$expression)) {
+                    return false;
+                }
+
+                Object this$lastMeetConditionTime = this.getLastMeetConditionTime();
+                Object other$lastMeetConditionTime = other.getLastMeetConditionTime();
+                if (this$lastMeetConditionTime == null) {
+                    if (other$lastMeetConditionTime != null) {
+                        return false;
+                    }
+                } else if (!this$lastMeetConditionTime.equals(other$lastMeetConditionTime)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof ConditionExpression;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $overMinutes = this.getOverMinutes();
+        result = result * 59 + ($overMinutes == null ? 43 : $overMinutes.hashCode());
+        Object $numberOfReminders = this.getNumberOfReminders();
+        result = result * 59 + ($numberOfReminders == null ? 43 : $numberOfReminders.hashCode());
+        Object $expression = this.getExpression();
+        result = result * 59 + ($expression == null ? 43 : $expression.hashCode());
+        Object $lastMeetConditionTime = this.getLastMeetConditionTime();
+        result = result * 59 + ($lastMeetConditionTime == null ? 43 : $lastMeetConditionTime.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "ConditionExpression(expression=" + this.getExpression() + ", overMinutes=" + this.getOverMinutes() + ", lastMeetConditionTime=" + this.getLastMeetConditionTime() + ", numberOfReminders=" + this.getNumberOfReminders() + ")";
+    }
+}

+ 277 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/DataPointVO.java

@@ -0,0 +1,277 @@
+package com.usky.rule.vo;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.usky.rule.vo.validator.GroupInsert;
+import com.usky.rule.vo.validator.GroupUpdate;
+import java.math.BigDecimal;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.PositiveOrZero;
+
+public class DataPointVO {
+    private @NotNull(
+            groups = {GroupInsert.class, GroupUpdate.class},
+            message = "时间不能为空"
+    ) @Pattern(
+            groups = {GroupInsert.class, GroupUpdate.class},
+            regexp = "^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)\\s+([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$",
+            message = "时间格式错误"
+    ) String time;
+    @JsonSerialize(
+            using = ToStringSerializer.class
+    )
+    private @PositiveOrZero(
+            groups = {GroupInsert.class, GroupUpdate.class},
+            message = "值不能小于0"
+    ) BigDecimal value;
+    private @NotNull String sampleTime;
+    @JsonIgnore
+    private BigDecimal previousValue;
+    @JsonIgnore
+    private BigDecimal nextValue;
+    @JsonIgnore
+    private BigDecimal oldValue;
+    @JsonIgnore
+    private DataPointVO previousVO;
+    @JsonIgnore
+    private DataPointVO nextVO;
+    @JsonIgnore
+    private String realTime;
+
+    public DataPointVO(String time, BigDecimal value) {
+        this.time = time;
+        this.value = value;
+    }
+
+    public DataPointVO(String time, BigDecimal old, BigDecimal value) {
+        this.time = time;
+        this.oldValue = old;
+        this.value = value;
+    }
+
+    public @NotNull(
+            groups = {GroupInsert.class, GroupUpdate.class},
+            message = "时间不能为空"
+    ) String getTime() {
+        return this.time;
+    }
+
+    public BigDecimal getValue() {
+        return this.value;
+    }
+
+    public @NotNull String getSampleTime() {
+        return this.sampleTime;
+    }
+
+    public BigDecimal getPreviousValue() {
+        return this.previousValue;
+    }
+
+    public BigDecimal getNextValue() {
+        return this.nextValue;
+    }
+
+    public BigDecimal getOldValue() {
+        return this.oldValue;
+    }
+
+    public DataPointVO getPreviousVO() {
+        return this.previousVO;
+    }
+
+    public DataPointVO getNextVO() {
+        return this.nextVO;
+    }
+
+    public String getRealTime() {
+        return this.realTime;
+    }
+
+    public void setTime(final @NotNull(
+            groups = {GroupInsert.class, GroupUpdate.class},
+            message = "时间不能为空"
+    ) String time) {
+        this.time = time;
+    }
+
+    public void setValue(final BigDecimal value) {
+        this.value = value;
+    }
+
+    public void setSampleTime(final @NotNull String sampleTime) {
+        this.sampleTime = sampleTime;
+    }
+
+    @JsonIgnore
+    public void setPreviousValue(final BigDecimal previousValue) {
+        this.previousValue = previousValue;
+    }
+
+    @JsonIgnore
+    public void setNextValue(final BigDecimal nextValue) {
+        this.nextValue = nextValue;
+    }
+
+    @JsonIgnore
+    public void setOldValue(final BigDecimal oldValue) {
+        this.oldValue = oldValue;
+    }
+
+    @JsonIgnore
+    public void setPreviousVO(final DataPointVO previousVO) {
+        this.previousVO = previousVO;
+    }
+
+    @JsonIgnore
+    public void setNextVO(final DataPointVO nextVO) {
+        this.nextVO = nextVO;
+    }
+
+    @JsonIgnore
+    public void setRealTime(final String realTime) {
+        this.realTime = realTime;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof DataPointVO)) {
+            return false;
+        } else {
+            DataPointVO other = (DataPointVO)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$time = this.getTime();
+                Object other$time = other.getTime();
+                if (this$time == null) {
+                    if (other$time != null) {
+                        return false;
+                    }
+                } else if (!this$time.equals(other$time)) {
+                    return false;
+                }
+
+                Object this$value = this.getValue();
+                Object other$value = other.getValue();
+                if (this$value == null) {
+                    if (other$value != null) {
+                        return false;
+                    }
+                } else if (!this$value.equals(other$value)) {
+                    return false;
+                }
+
+                Object this$sampleTime = this.getSampleTime();
+                Object other$sampleTime = other.getSampleTime();
+                if (this$sampleTime == null) {
+                    if (other$sampleTime != null) {
+                        return false;
+                    }
+                } else if (!this$sampleTime.equals(other$sampleTime)) {
+                    return false;
+                }
+
+                Object this$previousValue = this.getPreviousValue();
+                Object other$previousValue = other.getPreviousValue();
+                if (this$previousValue == null) {
+                    if (other$previousValue != null) {
+                        return false;
+                    }
+                } else if (!this$previousValue.equals(other$previousValue)) {
+                    return false;
+                }
+
+                Object this$nextValue = this.getNextValue();
+                Object other$nextValue = other.getNextValue();
+                if (this$nextValue == null) {
+                    if (other$nextValue != null) {
+                        return false;
+                    }
+                } else if (!this$nextValue.equals(other$nextValue)) {
+                    return false;
+                }
+
+                Object this$oldValue = this.getOldValue();
+                Object other$oldValue = other.getOldValue();
+                if (this$oldValue == null) {
+                    if (other$oldValue != null) {
+                        return false;
+                    }
+                } else if (!this$oldValue.equals(other$oldValue)) {
+                    return false;
+                }
+
+                Object this$previousVO = this.getPreviousVO();
+                Object other$previousVO = other.getPreviousVO();
+                if (this$previousVO == null) {
+                    if (other$previousVO != null) {
+                        return false;
+                    }
+                } else if (!this$previousVO.equals(other$previousVO)) {
+                    return false;
+                }
+
+                Object this$nextVO = this.getNextVO();
+                Object other$nextVO = other.getNextVO();
+                if (this$nextVO == null) {
+                    if (other$nextVO != null) {
+                        return false;
+                    }
+                } else if (!this$nextVO.equals(other$nextVO)) {
+                    return false;
+                }
+
+                Object this$realTime = this.getRealTime();
+                Object other$realTime = other.getRealTime();
+                if (this$realTime == null) {
+                    if (other$realTime != null) {
+                        return false;
+                    }
+                } else if (!this$realTime.equals(other$realTime)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof DataPointVO;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $time = this.getTime();
+        result = result * 59 + ($time == null ? 43 : $time.hashCode());
+        Object $value = this.getValue();
+        result = result * 59 + ($value == null ? 43 : $value.hashCode());
+        Object $sampleTime = this.getSampleTime();
+        result = result * 59 + ($sampleTime == null ? 43 : $sampleTime.hashCode());
+        Object $previousValue = this.getPreviousValue();
+        result = result * 59 + ($previousValue == null ? 43 : $previousValue.hashCode());
+        Object $nextValue = this.getNextValue();
+        result = result * 59 + ($nextValue == null ? 43 : $nextValue.hashCode());
+        Object $oldValue = this.getOldValue();
+        result = result * 59 + ($oldValue == null ? 43 : $oldValue.hashCode());
+        Object $previousVO = this.getPreviousVO();
+        result = result * 59 + ($previousVO == null ? 43 : $previousVO.hashCode());
+        Object $nextVO = this.getNextVO();
+        result = result * 59 + ($nextVO == null ? 43 : $nextVO.hashCode());
+        Object $realTime = this.getRealTime();
+        result = result * 59 + ($realTime == null ? 43 : $realTime.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "DataPointVO(time=" + this.getTime() + ", value=" + this.getValue() + ", sampleTime=" + this.getSampleTime() + ", previousValue=" + this.getPreviousValue() + ", nextValue=" + this.getNextValue() + ", oldValue=" + this.getOldValue() + ", previousVO=" + this.getPreviousVO() + ", nextVO=" + this.getNextVO() + ", realTime=" + this.getRealTime() + ")";
+    }
+
+    public DataPointVO() {
+    }
+}

+ 101 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/DeviceDataVO.java

@@ -0,0 +1,101 @@
+package com.usky.rule.vo;
+
+import java.util.Map;
+
+public class DeviceDataVO {
+    private String deviceId;
+    private String acqTime;
+    private Map<String, String> functions;
+
+    public DeviceDataVO() {
+    }
+
+    public String getDeviceId() {
+        return this.deviceId;
+    }
+
+    public String getAcqTime() {
+        return this.acqTime;
+    }
+
+    public Map<String, String> getFunctions() {
+        return this.functions;
+    }
+
+    public void setDeviceId(final String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public void setAcqTime(final String acqTime) {
+        this.acqTime = acqTime;
+    }
+
+    public void setFunctions(final Map<String, String> functions) {
+        this.functions = functions;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof DeviceDataVO)) {
+            return false;
+        } else {
+            DeviceDataVO other = (DeviceDataVO)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$deviceId = this.getDeviceId();
+                Object other$deviceId = other.getDeviceId();
+                if (this$deviceId == null) {
+                    if (other$deviceId != null) {
+                        return false;
+                    }
+                } else if (!this$deviceId.equals(other$deviceId)) {
+                    return false;
+                }
+
+                Object this$acqTime = this.getAcqTime();
+                Object other$acqTime = other.getAcqTime();
+                if (this$acqTime == null) {
+                    if (other$acqTime != null) {
+                        return false;
+                    }
+                } else if (!this$acqTime.equals(other$acqTime)) {
+                    return false;
+                }
+
+                Object this$functions = this.getFunctions();
+                Object other$functions = other.getFunctions();
+                if (this$functions == null) {
+                    if (other$functions != null) {
+                        return false;
+                    }
+                } else if (!this$functions.equals(other$functions)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof DeviceDataVO;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $deviceId = this.getDeviceId();
+        result = result * 59 + ($deviceId == null ? 43 : $deviceId.hashCode());
+        Object $acqTime = this.getAcqTime();
+        result = result * 59 + ($acqTime == null ? 43 : $acqTime.hashCode());
+        Object $functions = this.getFunctions();
+        result = result * 59 + ($functions == null ? 43 : $functions.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "DeviceDataVO(deviceId=" + this.getDeviceId() + ", acqTime=" + this.getAcqTime() + ", functions=" + this.getFunctions() + ")";
+    }
+}

+ 107 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/Expression.java

@@ -0,0 +1,107 @@
+package com.usky.rule.vo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Expression {
+    @JsonProperty("X")
+    private String x;
+    @JsonProperty("M")
+    private String m;
+    @JsonProperty("Y")
+    private String y;
+
+    public Expression() {
+    }
+
+    public String getX() {
+        return this.x;
+    }
+
+    public String getM() {
+        return this.m;
+    }
+
+    public String getY() {
+        return this.y;
+    }
+
+    @JsonProperty("X")
+    public void setX(final String x) {
+        this.x = x;
+    }
+
+    @JsonProperty("M")
+    public void setM(final String m) {
+        this.m = m;
+    }
+
+    @JsonProperty("Y")
+    public void setY(final String y) {
+        this.y = y;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof Expression)) {
+            return false;
+        } else {
+            Expression other = (Expression)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$x = this.getX();
+                Object other$x = other.getX();
+                if (this$x == null) {
+                    if (other$x != null) {
+                        return false;
+                    }
+                } else if (!this$x.equals(other$x)) {
+                    return false;
+                }
+
+                Object this$m = this.getM();
+                Object other$m = other.getM();
+                if (this$m == null) {
+                    if (other$m != null) {
+                        return false;
+                    }
+                } else if (!this$m.equals(other$m)) {
+                    return false;
+                }
+
+                Object this$y = this.getY();
+                Object other$y = other.getY();
+                if (this$y == null) {
+                    if (other$y != null) {
+                        return false;
+                    }
+                } else if (!this$y.equals(other$y)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof Expression;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $x = this.getX();
+        result = result * 59 + ($x == null ? 43 : $x.hashCode());
+        Object $m = this.getM();
+        result = result * 59 + ($m == null ? 43 : $m.hashCode());
+        Object $y = this.getY();
+        result = result * 59 + ($y == null ? 43 : $y.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "Expression(x=" + this.getX() + ", m=" + this.getM() + ", y=" + this.getY() + ")";
+    }
+}

+ 136 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/Result.java

@@ -0,0 +1,136 @@
+package com.usky.rule.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+@ApiModel
+public class Result<T> {
+    @ApiModelProperty("状态码")
+    private String code;
+    @ApiModelProperty("描述")
+    private String message;
+    @ApiModelProperty("数据")
+    private T data;
+
+    public static <T> Result<T> success() {
+        return (Result<T>) result("200000", "", (Object)null);
+    }
+
+    public static <T> Result<T> success(T data) {
+        return result("200000", "", data);
+    }
+
+    public static <T> Result<T> success(String message, T data) {
+        return result("200000", message, data);
+    }
+
+    public static <T> Result<T> failure(String code, String message) {
+        return (Result<T>) result(code, message, (Object)null);
+    }
+
+    public static <T> Result<T> failure(String code, String message, T data) {
+        return result(code, message, data);
+    }
+
+    private static <T> Result<T> result(String code, String message, T data) {
+        return new Result<T>(code, message, data);
+    }
+
+    public String getCode() {
+        return this.code;
+    }
+
+    public String getMessage() {
+        return this.message;
+    }
+
+    public T getData() {
+        return this.data;
+    }
+
+    public void setCode(final String code) {
+        this.code = code;
+    }
+
+    public void setMessage(final String message) {
+        this.message = message;
+    }
+
+    public void setData(final T data) {
+        this.data = data;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof Result)) {
+            return false;
+        } else {
+            Result<?> other = (Result)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$code = this.getCode();
+                Object other$code = other.getCode();
+                if (this$code == null) {
+                    if (other$code != null) {
+                        return false;
+                    }
+                } else if (!this$code.equals(other$code)) {
+                    return false;
+                }
+
+                Object this$message = this.getMessage();
+                Object other$message = other.getMessage();
+                if (this$message == null) {
+                    if (other$message != null) {
+                        return false;
+                    }
+                } else if (!this$message.equals(other$message)) {
+                    return false;
+                }
+
+                Object this$data = this.getData();
+                Object other$data = other.getData();
+                if (this$data == null) {
+                    if (other$data != null) {
+                        return false;
+                    }
+                } else if (!this$data.equals(other$data)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof Result;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $code = this.getCode();
+        result = result * 59 + ($code == null ? 43 : $code.hashCode());
+        Object $message = this.getMessage();
+        result = result * 59 + ($message == null ? 43 : $message.hashCode());
+        Object $data = this.getData();
+        result = result * 59 + ($data == null ? 43 : $data.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "Result(code=" + this.getCode() + ", message=" + this.getMessage() + ", data=" + this.getData() + ")";
+    }
+
+    public Result() {
+    }
+
+    public Result(final String code, final String message, final T data) {
+        this.code = code;
+        this.message = message;
+        this.data = data;
+    }
+}

+ 324 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineConditionVO.java

@@ -0,0 +1,324 @@
+package com.usky.rule.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.io.Serializable;
+
+@ApiModel
+public class RuleEngineConditionVO implements Serializable {
+    private static final long serialVersionUID = 596657710877126137L;
+    @ApiModelProperty("自增ID")
+    private Long id;
+    @ApiModelProperty("条件")
+    private String optionalCondition;
+    @ApiModelProperty("条件表达式")
+    private String expression;
+    @ApiModelProperty("条件描述")
+    private String descr;
+    @ApiModelProperty("类型 1:触发条件 2:约束条件")
+    private Short type;
+    @ApiModelProperty("更新人")
+    private Long updatedBy;
+    @ApiModelProperty("记录更新时间")
+    private String updateTime;
+    @ApiModelProperty("创建人")
+    private Long createdBy;
+    @ApiModelProperty("记录创建时间")
+    private String createTime;
+
+    public static RuleEngineConditionVOBuilder builder() {
+        return new RuleEngineConditionVOBuilder();
+    }
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public String getOptionalCondition() {
+        return this.optionalCondition;
+    }
+
+    public String getExpression() {
+        return this.expression;
+    }
+
+    public String getDescr() {
+        return this.descr;
+    }
+
+    public Short getType() {
+        return this.type;
+    }
+
+    public Long getUpdatedBy() {
+        return this.updatedBy;
+    }
+
+    public String getUpdateTime() {
+        return this.updateTime;
+    }
+
+    public Long getCreatedBy() {
+        return this.createdBy;
+    }
+
+    public String getCreateTime() {
+        return this.createTime;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public void setOptionalCondition(final String optionalCondition) {
+        this.optionalCondition = optionalCondition;
+    }
+
+    public void setExpression(final String expression) {
+        this.expression = expression;
+    }
+
+    public void setDescr(final String descr) {
+        this.descr = descr;
+    }
+
+    public void setType(final Short type) {
+        this.type = type;
+    }
+
+    public void setUpdatedBy(final Long updatedBy) {
+        this.updatedBy = updatedBy;
+    }
+
+    public void setUpdateTime(final String updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public void setCreatedBy(final Long createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public void setCreateTime(final String createTime) {
+        this.createTime = createTime;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof RuleEngineConditionVO)) {
+            return false;
+        } else {
+            RuleEngineConditionVO other = (RuleEngineConditionVO)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$id = this.getId();
+                Object other$id = other.getId();
+                if (this$id == null) {
+                    if (other$id != null) {
+                        return false;
+                    }
+                } else if (!this$id.equals(other$id)) {
+                    return false;
+                }
+
+                Object this$type = this.getType();
+                Object other$type = other.getType();
+                if (this$type == null) {
+                    if (other$type != null) {
+                        return false;
+                    }
+                } else if (!this$type.equals(other$type)) {
+                    return false;
+                }
+
+                Object this$updatedBy = this.getUpdatedBy();
+                Object other$updatedBy = other.getUpdatedBy();
+                if (this$updatedBy == null) {
+                    if (other$updatedBy != null) {
+                        return false;
+                    }
+                } else if (!this$updatedBy.equals(other$updatedBy)) {
+                    return false;
+                }
+
+                Object this$createdBy = this.getCreatedBy();
+                Object other$createdBy = other.getCreatedBy();
+                if (this$createdBy == null) {
+                    if (other$createdBy != null) {
+                        return false;
+                    }
+                } else if (!this$createdBy.equals(other$createdBy)) {
+                    return false;
+                }
+
+                Object this$optionalCondition = this.getOptionalCondition();
+                Object other$optionalCondition = other.getOptionalCondition();
+                if (this$optionalCondition == null) {
+                    if (other$optionalCondition != null) {
+                        return false;
+                    }
+                } else if (!this$optionalCondition.equals(other$optionalCondition)) {
+                    return false;
+                }
+
+                Object this$expression = this.getExpression();
+                Object other$expression = other.getExpression();
+                if (this$expression == null) {
+                    if (other$expression != null) {
+                        return false;
+                    }
+                } else if (!this$expression.equals(other$expression)) {
+                    return false;
+                }
+
+                Object this$descr = this.getDescr();
+                Object other$descr = other.getDescr();
+                if (this$descr == null) {
+                    if (other$descr != null) {
+                        return false;
+                    }
+                } else if (!this$descr.equals(other$descr)) {
+                    return false;
+                }
+
+                Object this$updateTime = this.getUpdateTime();
+                Object other$updateTime = other.getUpdateTime();
+                if (this$updateTime == null) {
+                    if (other$updateTime != null) {
+                        return false;
+                    }
+                } else if (!this$updateTime.equals(other$updateTime)) {
+                    return false;
+                }
+
+                Object this$createTime = this.getCreateTime();
+                Object other$createTime = other.getCreateTime();
+                if (this$createTime == null) {
+                    if (other$createTime != null) {
+                        return false;
+                    }
+                } else if (!this$createTime.equals(other$createTime)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof RuleEngineConditionVO;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $id = this.getId();
+        result = result * 59 + ($id == null ? 43 : $id.hashCode());
+        Object $type = this.getType();
+        result = result * 59 + ($type == null ? 43 : $type.hashCode());
+        Object $updatedBy = this.getUpdatedBy();
+        result = result * 59 + ($updatedBy == null ? 43 : $updatedBy.hashCode());
+        Object $createdBy = this.getCreatedBy();
+        result = result * 59 + ($createdBy == null ? 43 : $createdBy.hashCode());
+        Object $optionalCondition = this.getOptionalCondition();
+        result = result * 59 + ($optionalCondition == null ? 43 : $optionalCondition.hashCode());
+        Object $expression = this.getExpression();
+        result = result * 59 + ($expression == null ? 43 : $expression.hashCode());
+        Object $descr = this.getDescr();
+        result = result * 59 + ($descr == null ? 43 : $descr.hashCode());
+        Object $updateTime = this.getUpdateTime();
+        result = result * 59 + ($updateTime == null ? 43 : $updateTime.hashCode());
+        Object $createTime = this.getCreateTime();
+        result = result * 59 + ($createTime == null ? 43 : $createTime.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "RuleEngineConditionVO(id=" + this.getId() + ", optionalCondition=" + this.getOptionalCondition() + ", expression=" + this.getExpression() + ", descr=" + this.getDescr() + ", type=" + this.getType() + ", updatedBy=" + this.getUpdatedBy() + ", updateTime=" + this.getUpdateTime() + ", createdBy=" + this.getCreatedBy() + ", createTime=" + this.getCreateTime() + ")";
+    }
+
+    public RuleEngineConditionVO(final Long id, final String optionalCondition, final String expression, final String descr, final Short type, final Long updatedBy, final String updateTime, final Long createdBy, final String createTime) {
+        this.id = id;
+        this.optionalCondition = optionalCondition;
+        this.expression = expression;
+        this.descr = descr;
+        this.type = type;
+        this.updatedBy = updatedBy;
+        this.updateTime = updateTime;
+        this.createdBy = createdBy;
+        this.createTime = createTime;
+    }
+
+    public RuleEngineConditionVO() {
+    }
+
+    public static class RuleEngineConditionVOBuilder {
+        private Long id;
+        private String optionalCondition;
+        private String expression;
+        private String descr;
+        private Short type;
+        private Long updatedBy;
+        private String updateTime;
+        private Long createdBy;
+        private String createTime;
+
+        RuleEngineConditionVOBuilder() {
+        }
+
+        public RuleEngineConditionVOBuilder id(final Long id) {
+            this.id = id;
+            return this;
+        }
+
+        public RuleEngineConditionVOBuilder optionalCondition(final String optionalCondition) {
+            this.optionalCondition = optionalCondition;
+            return this;
+        }
+
+        public RuleEngineConditionVOBuilder expression(final String expression) {
+            this.expression = expression;
+            return this;
+        }
+
+        public RuleEngineConditionVOBuilder descr(final String descr) {
+            this.descr = descr;
+            return this;
+        }
+
+        public RuleEngineConditionVOBuilder type(final Short type) {
+            this.type = type;
+            return this;
+        }
+
+        public RuleEngineConditionVOBuilder updatedBy(final Long updatedBy) {
+            this.updatedBy = updatedBy;
+            return this;
+        }
+
+        public RuleEngineConditionVOBuilder updateTime(final String updateTime) {
+            this.updateTime = updateTime;
+            return this;
+        }
+
+        public RuleEngineConditionVOBuilder createdBy(final Long createdBy) {
+            this.createdBy = createdBy;
+            return this;
+        }
+
+        public RuleEngineConditionVOBuilder createTime(final String createTime) {
+            this.createTime = createTime;
+            return this;
+        }
+
+        public RuleEngineConditionVO build() {
+            return new RuleEngineConditionVO(this.id, this.optionalCondition, this.expression, this.descr, this.type, this.updatedBy, this.updateTime, this.createdBy, this.createTime);
+        }
+
+        public String toString() {
+            return "RuleEngineConditionVO.RuleEngineConditionVOBuilder(id=" + this.id + ", optionalCondition=" + this.optionalCondition + ", expression=" + this.expression + ", descr=" + this.descr + ", type=" + this.type + ", updatedBy=" + this.updatedBy + ", updateTime=" + this.updateTime + ", createdBy=" + this.createdBy + ", createTime=" + this.createTime + ")";
+        }
+    }
+}

+ 37 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineConfigDTO.java

@@ -0,0 +1,37 @@
+package com.usky.rule.vo;
+
+import com.usky.rule.domain.RuleEngine;
+import lombok.Data;
+
+import javax.validation.Valid;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 配置规则:规则主体 + 关联的 CRON 表达式列表 + 关联的设备列表
+ */
+@Data
+public class RuleEngineConfigDTO {
+    /** 规则主体(含 id 则为更新,无 id 则为新增) */
+    @Valid
+    private RuleEngine rule;
+    /** 该规则关联的 CRON 表达式列表,可为空 */
+    private List<String> cronExprs;
+    /** 该规则关联的设备(设备ID+属性标识+产品ID),可为空 */
+    private List<RuleEngineDeviceItem> devices;
+
+    public List<String> getCronExprs() {
+        return cronExprs != null ? cronExprs : new ArrayList<>();
+    }
+
+    public List<RuleEngineDeviceItem> getDevices() {
+        return devices != null ? devices : new ArrayList<>();
+    }
+
+    @Data
+    public static class RuleEngineDeviceItem {
+        private String deviceId;
+        private String identifier;
+        private Long productId;
+    }
+}

+ 395 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineContent.java

@@ -0,0 +1,395 @@
+package com.usky.rule.vo;
+
+import java.util.List;
+
+public class RuleEngineContent {
+    private Trigger trigger;
+    private List<Constraints> constraints;
+    private List<Actions> actions;
+
+    public RuleEngineContent() {
+    }
+
+    public Trigger getTrigger() {
+        return this.trigger;
+    }
+
+    public List<Constraints> getConstraints() {
+        return this.constraints;
+    }
+
+    public List<Actions> getActions() {
+        return this.actions;
+    }
+
+    public void setTrigger(final Trigger trigger) {
+        this.trigger = trigger;
+    }
+
+    public void setConstraints(final List<Constraints> constraints) {
+        this.constraints = constraints;
+    }
+
+    public void setActions(final List<Actions> actions) {
+        this.actions = actions;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof RuleEngineContent)) {
+            return false;
+        } else {
+            RuleEngineContent other = (RuleEngineContent)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$trigger = this.getTrigger();
+                Object other$trigger = other.getTrigger();
+                if (this$trigger == null) {
+                    if (other$trigger != null) {
+                        return false;
+                    }
+                } else if (!this$trigger.equals(other$trigger)) {
+                    return false;
+                }
+
+                Object this$constraints = this.getConstraints();
+                Object other$constraints = other.getConstraints();
+                if (this$constraints == null) {
+                    if (other$constraints != null) {
+                        return false;
+                    }
+                } else if (!this$constraints.equals(other$constraints)) {
+                    return false;
+                }
+
+                Object this$actions = this.getActions();
+                Object other$actions = other.getActions();
+                if (this$actions == null) {
+                    if (other$actions != null) {
+                        return false;
+                    }
+                } else if (!this$actions.equals(other$actions)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof RuleEngineContent;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $trigger = this.getTrigger();
+        result = result * 59 + ($trigger == null ? 43 : $trigger.hashCode());
+        Object $constraints = this.getConstraints();
+        result = result * 59 + ($constraints == null ? 43 : $constraints.hashCode());
+        Object $actions = this.getActions();
+        result = result * 59 + ($actions == null ? 43 : $actions.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "RuleEngineContent(trigger=" + this.getTrigger() + ", constraints=" + this.getConstraints() + ", actions=" + this.getActions() + ")";
+    }
+
+    public static class Trigger {
+        private String autoTrigger;
+        private String type;
+        private String content;
+        private String time;
+
+        public Trigger() {
+        }
+
+        public String getAutoTrigger() {
+            return this.autoTrigger;
+        }
+
+        public String getType() {
+            return this.type;
+        }
+
+        public String getContent() {
+            return this.content;
+        }
+
+        public String getTime() {
+            return this.time;
+        }
+
+        public void setAutoTrigger(final String autoTrigger) {
+            this.autoTrigger = autoTrigger;
+        }
+
+        public void setType(final String type) {
+            this.type = type;
+        }
+
+        public void setContent(final String content) {
+            this.content = content;
+        }
+
+        public void setTime(final String time) {
+            this.time = time;
+        }
+
+        public boolean equals(final Object o) {
+            if (o == this) {
+                return true;
+            } else if (!(o instanceof Trigger)) {
+                return false;
+            } else {
+                Trigger other = (Trigger)o;
+                if (!other.canEqual(this)) {
+                    return false;
+                } else {
+                    Object this$autoTrigger = this.getAutoTrigger();
+                    Object other$autoTrigger = other.getAutoTrigger();
+                    if (this$autoTrigger == null) {
+                        if (other$autoTrigger != null) {
+                            return false;
+                        }
+                    } else if (!this$autoTrigger.equals(other$autoTrigger)) {
+                        return false;
+                    }
+
+                    Object this$type = this.getType();
+                    Object other$type = other.getType();
+                    if (this$type == null) {
+                        if (other$type != null) {
+                            return false;
+                        }
+                    } else if (!this$type.equals(other$type)) {
+                        return false;
+                    }
+
+                    Object this$content = this.getContent();
+                    Object other$content = other.getContent();
+                    if (this$content == null) {
+                        if (other$content != null) {
+                            return false;
+                        }
+                    } else if (!this$content.equals(other$content)) {
+                        return false;
+                    }
+
+                    Object this$time = this.getTime();
+                    Object other$time = other.getTime();
+                    if (this$time == null) {
+                        if (other$time != null) {
+                            return false;
+                        }
+                    } else if (!this$time.equals(other$time)) {
+                        return false;
+                    }
+
+                    return true;
+                }
+            }
+        }
+
+        protected boolean canEqual(final Object other) {
+            return other instanceof Trigger;
+        }
+
+        public int hashCode() {
+            int PRIME = 59;
+            int result = 1;
+            Object $autoTrigger = this.getAutoTrigger();
+            result = result * 59 + ($autoTrigger == null ? 43 : $autoTrigger.hashCode());
+            Object $type = this.getType();
+            result = result * 59 + ($type == null ? 43 : $type.hashCode());
+            Object $content = this.getContent();
+            result = result * 59 + ($content == null ? 43 : $content.hashCode());
+            Object $time = this.getTime();
+            result = result * 59 + ($time == null ? 43 : $time.hashCode());
+            return result;
+        }
+
+        public String toString() {
+            return "RuleEngineContent.Trigger(autoTrigger=" + this.getAutoTrigger() + ", type=" + this.getType() + ", content=" + this.getContent() + ", time=" + this.getTime() + ")";
+        }
+    }
+
+    public static class Constraints {
+        private String type;
+        private String content;
+
+        public Constraints() {
+        }
+
+        public String getType() {
+            return this.type;
+        }
+
+        public String getContent() {
+            return this.content;
+        }
+
+        public void setType(final String type) {
+            this.type = type;
+        }
+
+        public void setContent(final String content) {
+            this.content = content;
+        }
+
+        public boolean equals(final Object o) {
+            if (o == this) {
+                return true;
+            } else if (!(o instanceof Constraints)) {
+                return false;
+            } else {
+                Constraints other = (Constraints)o;
+                if (!other.canEqual(this)) {
+                    return false;
+                } else {
+                    Object this$type = this.getType();
+                    Object other$type = other.getType();
+                    if (this$type == null) {
+                        if (other$type != null) {
+                            return false;
+                        }
+                    } else if (!this$type.equals(other$type)) {
+                        return false;
+                    }
+
+                    Object this$content = this.getContent();
+                    Object other$content = other.getContent();
+                    if (this$content == null) {
+                        if (other$content != null) {
+                            return false;
+                        }
+                    } else if (!this$content.equals(other$content)) {
+                        return false;
+                    }
+
+                    return true;
+                }
+            }
+        }
+
+        protected boolean canEqual(final Object other) {
+            return other instanceof Constraints;
+        }
+
+        public int hashCode() {
+            int PRIME = 59;
+            int result = 1;
+            Object $type = this.getType();
+            result = result * 59 + ($type == null ? 43 : $type.hashCode());
+            Object $content = this.getContent();
+            result = result * 59 + ($content == null ? 43 : $content.hashCode());
+            return result;
+        }
+
+        public String toString() {
+            return "RuleEngineContent.Constraints(type=" + this.getType() + ", content=" + this.getContent() + ")";
+        }
+    }
+
+    public static class Actions {
+        private String type;
+        private String content;
+        private String time;
+
+        public Actions() {
+        }
+
+        public String getType() {
+            return this.type;
+        }
+
+        public String getContent() {
+            return this.content;
+        }
+
+        public String getTime() {
+            return this.time;
+        }
+
+        public void setType(final String type) {
+            this.type = type;
+        }
+
+        public void setContent(final String content) {
+            this.content = content;
+        }
+
+        public void setTime(final String time) {
+            this.time = time;
+        }
+
+        public boolean equals(final Object o) {
+            if (o == this) {
+                return true;
+            } else if (!(o instanceof Actions)) {
+                return false;
+            } else {
+                Actions other = (Actions)o;
+                if (!other.canEqual(this)) {
+                    return false;
+                } else {
+                    Object this$type = this.getType();
+                    Object other$type = other.getType();
+                    if (this$type == null) {
+                        if (other$type != null) {
+                            return false;
+                        }
+                    } else if (!this$type.equals(other$type)) {
+                        return false;
+                    }
+
+                    Object this$content = this.getContent();
+                    Object other$content = other.getContent();
+                    if (this$content == null) {
+                        if (other$content != null) {
+                            return false;
+                        }
+                    } else if (!this$content.equals(other$content)) {
+                        return false;
+                    }
+
+                    Object this$time = this.getTime();
+                    Object other$time = other.getTime();
+                    if (this$time == null) {
+                        if (other$time != null) {
+                            return false;
+                        }
+                    } else if (!this$time.equals(other$time)) {
+                        return false;
+                    }
+
+                    return true;
+                }
+            }
+        }
+
+        protected boolean canEqual(final Object other) {
+            return other instanceof Actions;
+        }
+
+        public int hashCode() {
+            int PRIME = 59;
+            int result = 1;
+            Object $type = this.getType();
+            result = result * 59 + ($type == null ? 43 : $type.hashCode());
+            Object $content = this.getContent();
+            result = result * 59 + ($content == null ? 43 : $content.hashCode());
+            Object $time = this.getTime();
+            result = result * 59 + ($time == null ? 43 : $time.hashCode());
+            return result;
+        }
+
+        public String toString() {
+            return "RuleEngineContent.Actions(type=" + this.getType() + ", content=" + this.getContent() + ", time=" + this.getTime() + ")";
+        }
+    }
+}

+ 353 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineCronVO.java

@@ -0,0 +1,353 @@
+package com.usky.rule.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.io.Serializable;
+
+@ApiModel
+public class RuleEngineCronVO implements Serializable {
+    private static final long serialVersionUID = -71778082126945026L;
+    @ApiModelProperty("自增ID")
+    private Long id;
+    @ApiModelProperty("规则引擎ID")
+    private Long ruleEngineId;
+    @ApiModelProperty("规则引擎ID")
+    private Long ruleEngineName;
+    @ApiModelProperty("CRON表达式")
+    private String cron;
+    @ApiModelProperty("项目ID")
+    private Long projectId;
+    @ApiModelProperty("空间ID")
+    private Long spaceId;
+    @ApiModelProperty("更新人")
+    private Long updatedBy;
+    @ApiModelProperty("记录更新时间")
+    private String updateTime;
+    @ApiModelProperty("创建人")
+    private Long createdBy;
+    @ApiModelProperty("记录创建时间")
+    private String createTime;
+
+    public static RuleEngineCronVOBuilder builder() {
+        return new RuleEngineCronVOBuilder();
+    }
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public Long getRuleEngineId() {
+        return this.ruleEngineId;
+    }
+
+    public Long getRuleEngineName() {
+        return this.ruleEngineName;
+    }
+
+    public String getCron() {
+        return this.cron;
+    }
+
+    public Long getProjectId() {
+        return this.projectId;
+    }
+
+    public Long getSpaceId() {
+        return this.spaceId;
+    }
+
+    public Long getUpdatedBy() {
+        return this.updatedBy;
+    }
+
+    public String getUpdateTime() {
+        return this.updateTime;
+    }
+
+    public Long getCreatedBy() {
+        return this.createdBy;
+    }
+
+    public String getCreateTime() {
+        return this.createTime;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public void setRuleEngineId(final Long ruleEngineId) {
+        this.ruleEngineId = ruleEngineId;
+    }
+
+    public void setRuleEngineName(final Long ruleEngineName) {
+        this.ruleEngineName = ruleEngineName;
+    }
+
+    public void setCron(final String cron) {
+        this.cron = cron;
+    }
+
+    public void setProjectId(final Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public void setSpaceId(final Long spaceId) {
+        this.spaceId = spaceId;
+    }
+
+    public void setUpdatedBy(final Long updatedBy) {
+        this.updatedBy = updatedBy;
+    }
+
+    public void setUpdateTime(final String updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public void setCreatedBy(final Long createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public void setCreateTime(final String createTime) {
+        this.createTime = createTime;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof RuleEngineCronVO)) {
+            return false;
+        } else {
+            RuleEngineCronVO other = (RuleEngineCronVO)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$id = this.getId();
+                Object other$id = other.getId();
+                if (this$id == null) {
+                    if (other$id != null) {
+                        return false;
+                    }
+                } else if (!this$id.equals(other$id)) {
+                    return false;
+                }
+
+                Object this$ruleEngineId = this.getRuleEngineId();
+                Object other$ruleEngineId = other.getRuleEngineId();
+                if (this$ruleEngineId == null) {
+                    if (other$ruleEngineId != null) {
+                        return false;
+                    }
+                } else if (!this$ruleEngineId.equals(other$ruleEngineId)) {
+                    return false;
+                }
+
+                Object this$ruleEngineName = this.getRuleEngineName();
+                Object other$ruleEngineName = other.getRuleEngineName();
+                if (this$ruleEngineName == null) {
+                    if (other$ruleEngineName != null) {
+                        return false;
+                    }
+                } else if (!this$ruleEngineName.equals(other$ruleEngineName)) {
+                    return false;
+                }
+
+                Object this$projectId = this.getProjectId();
+                Object other$projectId = other.getProjectId();
+                if (this$projectId == null) {
+                    if (other$projectId != null) {
+                        return false;
+                    }
+                } else if (!this$projectId.equals(other$projectId)) {
+                    return false;
+                }
+
+                Object this$spaceId = this.getSpaceId();
+                Object other$spaceId = other.getSpaceId();
+                if (this$spaceId == null) {
+                    if (other$spaceId != null) {
+                        return false;
+                    }
+                } else if (!this$spaceId.equals(other$spaceId)) {
+                    return false;
+                }
+
+                Object this$updatedBy = this.getUpdatedBy();
+                Object other$updatedBy = other.getUpdatedBy();
+                if (this$updatedBy == null) {
+                    if (other$updatedBy != null) {
+                        return false;
+                    }
+                } else if (!this$updatedBy.equals(other$updatedBy)) {
+                    return false;
+                }
+
+                Object this$createdBy = this.getCreatedBy();
+                Object other$createdBy = other.getCreatedBy();
+                if (this$createdBy == null) {
+                    if (other$createdBy != null) {
+                        return false;
+                    }
+                } else if (!this$createdBy.equals(other$createdBy)) {
+                    return false;
+                }
+
+                Object this$cron = this.getCron();
+                Object other$cron = other.getCron();
+                if (this$cron == null) {
+                    if (other$cron != null) {
+                        return false;
+                    }
+                } else if (!this$cron.equals(other$cron)) {
+                    return false;
+                }
+
+                Object this$updateTime = this.getUpdateTime();
+                Object other$updateTime = other.getUpdateTime();
+                if (this$updateTime == null) {
+                    if (other$updateTime != null) {
+                        return false;
+                    }
+                } else if (!this$updateTime.equals(other$updateTime)) {
+                    return false;
+                }
+
+                Object this$createTime = this.getCreateTime();
+                Object other$createTime = other.getCreateTime();
+                if (this$createTime == null) {
+                    if (other$createTime != null) {
+                        return false;
+                    }
+                } else if (!this$createTime.equals(other$createTime)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof RuleEngineCronVO;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $id = this.getId();
+        result = result * 59 + ($id == null ? 43 : $id.hashCode());
+        Object $ruleEngineId = this.getRuleEngineId();
+        result = result * 59 + ($ruleEngineId == null ? 43 : $ruleEngineId.hashCode());
+        Object $ruleEngineName = this.getRuleEngineName();
+        result = result * 59 + ($ruleEngineName == null ? 43 : $ruleEngineName.hashCode());
+        Object $projectId = this.getProjectId();
+        result = result * 59 + ($projectId == null ? 43 : $projectId.hashCode());
+        Object $spaceId = this.getSpaceId();
+        result = result * 59 + ($spaceId == null ? 43 : $spaceId.hashCode());
+        Object $updatedBy = this.getUpdatedBy();
+        result = result * 59 + ($updatedBy == null ? 43 : $updatedBy.hashCode());
+        Object $createdBy = this.getCreatedBy();
+        result = result * 59 + ($createdBy == null ? 43 : $createdBy.hashCode());
+        Object $cron = this.getCron();
+        result = result * 59 + ($cron == null ? 43 : $cron.hashCode());
+        Object $updateTime = this.getUpdateTime();
+        result = result * 59 + ($updateTime == null ? 43 : $updateTime.hashCode());
+        Object $createTime = this.getCreateTime();
+        result = result * 59 + ($createTime == null ? 43 : $createTime.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "RuleEngineCronVO(id=" + this.getId() + ", ruleEngineId=" + this.getRuleEngineId() + ", ruleEngineName=" + this.getRuleEngineName() + ", cron=" + this.getCron() + ", projectId=" + this.getProjectId() + ", spaceId=" + this.getSpaceId() + ", updatedBy=" + this.getUpdatedBy() + ", updateTime=" + this.getUpdateTime() + ", createdBy=" + this.getCreatedBy() + ", createTime=" + this.getCreateTime() + ")";
+    }
+
+    public RuleEngineCronVO(final Long id, final Long ruleEngineId, final Long ruleEngineName, final String cron, final Long projectId, final Long spaceId, final Long updatedBy, final String updateTime, final Long createdBy, final String createTime) {
+        this.id = id;
+        this.ruleEngineId = ruleEngineId;
+        this.ruleEngineName = ruleEngineName;
+        this.cron = cron;
+        this.projectId = projectId;
+        this.spaceId = spaceId;
+        this.updatedBy = updatedBy;
+        this.updateTime = updateTime;
+        this.createdBy = createdBy;
+        this.createTime = createTime;
+    }
+
+    public RuleEngineCronVO() {
+    }
+
+    public static class RuleEngineCronVOBuilder {
+        private Long id;
+        private Long ruleEngineId;
+        private Long ruleEngineName;
+        private String cron;
+        private Long projectId;
+        private Long spaceId;
+        private Long updatedBy;
+        private String updateTime;
+        private Long createdBy;
+        private String createTime;
+
+        RuleEngineCronVOBuilder() {
+        }
+
+        public RuleEngineCronVOBuilder id(final Long id) {
+            this.id = id;
+            return this;
+        }
+
+        public RuleEngineCronVOBuilder ruleEngineId(final Long ruleEngineId) {
+            this.ruleEngineId = ruleEngineId;
+            return this;
+        }
+
+        public RuleEngineCronVOBuilder ruleEngineName(final Long ruleEngineName) {
+            this.ruleEngineName = ruleEngineName;
+            return this;
+        }
+
+        public RuleEngineCronVOBuilder cron(final String cron) {
+            this.cron = cron;
+            return this;
+        }
+
+        public RuleEngineCronVOBuilder projectId(final Long projectId) {
+            this.projectId = projectId;
+            return this;
+        }
+
+        public RuleEngineCronVOBuilder spaceId(final Long spaceId) {
+            this.spaceId = spaceId;
+            return this;
+        }
+
+        public RuleEngineCronVOBuilder updatedBy(final Long updatedBy) {
+            this.updatedBy = updatedBy;
+            return this;
+        }
+
+        public RuleEngineCronVOBuilder updateTime(final String updateTime) {
+            this.updateTime = updateTime;
+            return this;
+        }
+
+        public RuleEngineCronVOBuilder createdBy(final Long createdBy) {
+            this.createdBy = createdBy;
+            return this;
+        }
+
+        public RuleEngineCronVOBuilder createTime(final String createTime) {
+            this.createTime = createTime;
+            return this;
+        }
+
+        public RuleEngineCronVO build() {
+            return new RuleEngineCronVO(this.id, this.ruleEngineId, this.ruleEngineName, this.cron, this.projectId, this.spaceId, this.updatedBy, this.updateTime, this.createdBy, this.createTime);
+        }
+
+        public String toString() {
+            return "RuleEngineCronVO.RuleEngineCronVOBuilder(id=" + this.id + ", ruleEngineId=" + this.ruleEngineId + ", ruleEngineName=" + this.ruleEngineName + ", cron=" + this.cron + ", projectId=" + this.projectId + ", spaceId=" + this.spaceId + ", updatedBy=" + this.updatedBy + ", updateTime=" + this.updateTime + ", createdBy=" + this.createdBy + ", createTime=" + this.createTime + ")";
+        }
+    }
+}

+ 316 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineDTO.java

@@ -0,0 +1,316 @@
+package com.usky.rule.vo;
+
+import com.fasterxml.jackson.annotation.JsonRawValue;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.usky.rule.util.JsonToStringDeserializer;
+import com.usky.rule.vo.validator.GroupInsert;
+import com.usky.rule.vo.validator.GroupUpdate;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.io.Serializable;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@ApiModel
+public class RuleEngineDTO implements Serializable {
+    private static final long serialVersionUID = -80869839545181400L;
+    @ApiModelProperty("自增ID")
+    private @NotNull(
+            groups = {GroupUpdate.class},
+            message = "id不能为空"
+    ) Long id;
+    @ApiModelProperty("项目ID")
+    private Long projectId;
+    @ApiModelProperty("名称")
+    private @NotEmpty(
+            groups = {GroupUpdate.class, GroupInsert.class},
+            message = "名称不能为空"
+    ) String name;
+    @ApiModelProperty("空间ID")
+    private @NotNull(
+            groups = {GroupInsert.class},
+            message = "空间不能为空"
+    ) Long spaceId;
+    @ApiModelProperty("规则状态 1:启用 0:停用")
+    private Integer status;
+    @ApiModelProperty("规则描述")
+    private String descr;
+    @ApiModelProperty("规则详情")
+    @JsonDeserialize(
+            using = JsonToStringDeserializer.class
+    )
+    @JsonRawValue
+    private String detail;
+
+    public static RuleEngineDTOBuilder builder() {
+        return new RuleEngineDTOBuilder();
+    }
+
+    public @NotNull(
+            groups = {GroupUpdate.class},
+            message = "id不能为空"
+    ) Long getId() {
+        return this.id;
+    }
+
+    public Long getProjectId() {
+        return this.projectId;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public @NotNull(
+            groups = {GroupInsert.class},
+            message = "空间不能为空"
+    ) Long getSpaceId() {
+        return this.spaceId;
+    }
+
+    public Integer getStatus() {
+        return this.status;
+    }
+
+    public String getDescr() {
+        return this.descr;
+    }
+
+    public String getDetail() {
+        return this.detail;
+    }
+
+    public void setId(final @NotNull(
+            groups = {GroupUpdate.class},
+            message = "id不能为空"
+    ) Long id) {
+        this.id = id;
+    }
+
+    public void setProjectId(final Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public void setSpaceId(final @NotNull(
+            groups = {GroupInsert.class},
+            message = "空间不能为空"
+    ) Long spaceId) {
+        this.spaceId = spaceId;
+    }
+
+    public void setStatus(final Integer status) {
+        this.status = status;
+    }
+
+    public void setDescr(final String descr) {
+        this.descr = descr;
+    }
+
+    @JsonDeserialize(
+            using = JsonToStringDeserializer.class
+    )
+    public void setDetail(final String detail) {
+        this.detail = detail;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof RuleEngineDTO)) {
+            return false;
+        } else {
+            RuleEngineDTO other = (RuleEngineDTO)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$id = this.getId();
+                Object other$id = other.getId();
+                if (this$id == null) {
+                    if (other$id != null) {
+                        return false;
+                    }
+                } else if (!this$id.equals(other$id)) {
+                    return false;
+                }
+
+                Object this$projectId = this.getProjectId();
+                Object other$projectId = other.getProjectId();
+                if (this$projectId == null) {
+                    if (other$projectId != null) {
+                        return false;
+                    }
+                } else if (!this$projectId.equals(other$projectId)) {
+                    return false;
+                }
+
+                Object this$spaceId = this.getSpaceId();
+                Object other$spaceId = other.getSpaceId();
+                if (this$spaceId == null) {
+                    if (other$spaceId != null) {
+                        return false;
+                    }
+                } else if (!this$spaceId.equals(other$spaceId)) {
+                    return false;
+                }
+
+                Object this$status = this.getStatus();
+                Object other$status = other.getStatus();
+                if (this$status == null) {
+                    if (other$status != null) {
+                        return false;
+                    }
+                } else if (!this$status.equals(other$status)) {
+                    return false;
+                }
+
+                Object this$name = this.getName();
+                Object other$name = other.getName();
+                if (this$name == null) {
+                    if (other$name != null) {
+                        return false;
+                    }
+                } else if (!this$name.equals(other$name)) {
+                    return false;
+                }
+
+                Object this$descr = this.getDescr();
+                Object other$descr = other.getDescr();
+                if (this$descr == null) {
+                    if (other$descr != null) {
+                        return false;
+                    }
+                } else if (!this$descr.equals(other$descr)) {
+                    return false;
+                }
+
+                Object this$detail = this.getDetail();
+                Object other$detail = other.getDetail();
+                if (this$detail == null) {
+                    if (other$detail != null) {
+                        return false;
+                    }
+                } else if (!this$detail.equals(other$detail)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof RuleEngineDTO;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $id = this.getId();
+        result = result * 59 + ($id == null ? 43 : $id.hashCode());
+        Object $projectId = this.getProjectId();
+        result = result * 59 + ($projectId == null ? 43 : $projectId.hashCode());
+        Object $spaceId = this.getSpaceId();
+        result = result * 59 + ($spaceId == null ? 43 : $spaceId.hashCode());
+        Object $status = this.getStatus();
+        result = result * 59 + ($status == null ? 43 : $status.hashCode());
+        Object $name = this.getName();
+        result = result * 59 + ($name == null ? 43 : $name.hashCode());
+        Object $descr = this.getDescr();
+        result = result * 59 + ($descr == null ? 43 : $descr.hashCode());
+        Object $detail = this.getDetail();
+        result = result * 59 + ($detail == null ? 43 : $detail.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "RuleEngineDTO(id=" + this.getId() + ", projectId=" + this.getProjectId() + ", name=" + this.getName() + ", spaceId=" + this.getSpaceId() + ", status=" + this.getStatus() + ", descr=" + this.getDescr() + ", detail=" + this.getDetail() + ")";
+    }
+
+    public RuleEngineDTO(final @NotNull(
+            groups = {GroupUpdate.class},
+            message = "id不能为空"
+    ) Long id, final Long projectId, final String name, final @NotNull(
+            groups = {GroupInsert.class},
+            message = "空间不能为空"
+    ) Long spaceId, final Integer status, final String descr, final String detail) {
+        this.id = id;
+        this.projectId = projectId;
+        this.name = name;
+        this.spaceId = spaceId;
+        this.status = status;
+        this.descr = descr;
+        this.detail = detail;
+    }
+
+    public RuleEngineDTO() {
+    }
+
+    public static class RuleEngineDTOBuilder {
+        private Long id;
+        private Long projectId;
+        private String name;
+        private Long spaceId;
+        private Integer status;
+        private String descr;
+        private String detail;
+
+        RuleEngineDTOBuilder() {
+        }
+
+        public RuleEngineDTOBuilder id(final @NotNull(
+                groups = {GroupUpdate.class},
+                message = "id不能为空"
+        ) Long id) {
+            this.id = id;
+            return this;
+        }
+
+        public RuleEngineDTOBuilder projectId(final Long projectId) {
+            this.projectId = projectId;
+            return this;
+        }
+
+        public RuleEngineDTOBuilder name(final String name) {
+            this.name = name;
+            return this;
+        }
+
+        public RuleEngineDTOBuilder spaceId(final @NotNull(
+                groups = {GroupInsert.class},
+                message = "空间不能为空"
+        ) Long spaceId) {
+            this.spaceId = spaceId;
+            return this;
+        }
+
+        public RuleEngineDTOBuilder status(final Integer status) {
+            this.status = status;
+            return this;
+        }
+
+        public RuleEngineDTOBuilder descr(final String descr) {
+            this.descr = descr;
+            return this;
+        }
+
+        @JsonDeserialize(
+                using = JsonToStringDeserializer.class
+        )
+        public RuleEngineDTOBuilder detail(final String detail) {
+            this.detail = detail;
+            return this;
+        }
+
+        public RuleEngineDTO build() {
+            return new RuleEngineDTO(this.id, this.projectId, this.name, this.spaceId, this.status, this.descr, this.detail);
+        }
+
+        public String toString() {
+            return "RuleEngineDTO.RuleEngineDTOBuilder(id=" + this.id + ", projectId=" + this.projectId + ", name=" + this.name + ", spaceId=" + this.spaceId + ", status=" + this.status + ", descr=" + this.descr + ", detail=" + this.detail + ")";
+        }
+    }
+}

+ 202 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineDetail.java

@@ -0,0 +1,202 @@
+package com.usky.rule.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.usky.rule.util.JsonToStringDeserializer;
+import java.util.List;
+
+public class RuleEngineDetail {
+    @JsonProperty("triggers")
+    @JsonInclude(Include.NON_NULL)
+    private List<CommonVO> triggers;
+    @JsonProperty("constraints")
+    @JsonInclude(Include.NON_NULL)
+    private List<CommonVO> constraints;
+    @JsonProperty("actions")
+    @JsonInclude(Include.NON_NULL)
+    private List<CommonVO> actions;
+
+    public RuleEngineDetail() {
+    }
+
+    public List<CommonVO> getTriggers() {
+        return this.triggers;
+    }
+
+    public List<CommonVO> getConstraints() {
+        return this.constraints;
+    }
+
+    public List<CommonVO> getActions() {
+        return this.actions;
+    }
+
+    @JsonProperty("triggers")
+    public void setTriggers(final List<CommonVO> triggers) {
+        this.triggers = triggers;
+    }
+
+    @JsonProperty("constraints")
+    public void setConstraints(final List<CommonVO> constraints) {
+        this.constraints = constraints;
+    }
+
+    @JsonProperty("actions")
+    public void setActions(final List<CommonVO> actions) {
+        this.actions = actions;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof RuleEngineDetail)) {
+            return false;
+        } else {
+            RuleEngineDetail other = (RuleEngineDetail)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$triggers = this.getTriggers();
+                Object other$triggers = other.getTriggers();
+                if (this$triggers == null) {
+                    if (other$triggers != null) {
+                        return false;
+                    }
+                } else if (!this$triggers.equals(other$triggers)) {
+                    return false;
+                }
+
+                Object this$constraints = this.getConstraints();
+                Object other$constraints = other.getConstraints();
+                if (this$constraints == null) {
+                    if (other$constraints != null) {
+                        return false;
+                    }
+                } else if (!this$constraints.equals(other$constraints)) {
+                    return false;
+                }
+
+                Object this$actions = this.getActions();
+                Object other$actions = other.getActions();
+                if (this$actions == null) {
+                    if (other$actions != null) {
+                        return false;
+                    }
+                } else if (!this$actions.equals(other$actions)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof RuleEngineDetail;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $triggers = this.getTriggers();
+        result = result * 59 + ($triggers == null ? 43 : $triggers.hashCode());
+        Object $constraints = this.getConstraints();
+        result = result * 59 + ($constraints == null ? 43 : $constraints.hashCode());
+        Object $actions = this.getActions();
+        result = result * 59 + ($actions == null ? 43 : $actions.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "RuleEngineDetail(triggers=" + this.getTriggers() + ", constraints=" + this.getConstraints() + ", actions=" + this.getActions() + ")";
+    }
+
+    public static class CommonVO {
+        @JsonProperty("type")
+        private String type;
+        @JsonProperty("detail")
+        @JsonDeserialize(
+                using = JsonToStringDeserializer.class
+        )
+        private String detail;
+
+        public CommonVO() {
+        }
+
+        public String getType() {
+            return this.type;
+        }
+
+        public String getDetail() {
+            return this.detail;
+        }
+
+        @JsonProperty("type")
+        public void setType(final String type) {
+            this.type = type;
+        }
+
+        @JsonProperty("detail")
+        @JsonDeserialize(
+                using = JsonToStringDeserializer.class
+        )
+        public void setDetail(final String detail) {
+            this.detail = detail;
+        }
+
+        public boolean equals(final Object o) {
+            if (o == this) {
+                return true;
+            } else if (!(o instanceof CommonVO)) {
+                return false;
+            } else {
+                CommonVO other = (CommonVO)o;
+                if (!other.canEqual(this)) {
+                    return false;
+                } else {
+                    Object this$type = this.getType();
+                    Object other$type = other.getType();
+                    if (this$type == null) {
+                        if (other$type != null) {
+                            return false;
+                        }
+                    } else if (!this$type.equals(other$type)) {
+                        return false;
+                    }
+
+                    Object this$detail = this.getDetail();
+                    Object other$detail = other.getDetail();
+                    if (this$detail == null) {
+                        if (other$detail != null) {
+                            return false;
+                        }
+                    } else if (!this$detail.equals(other$detail)) {
+                        return false;
+                    }
+
+                    return true;
+                }
+            }
+        }
+
+        protected boolean canEqual(final Object other) {
+            return other instanceof CommonVO;
+        }
+
+        public int hashCode() {
+            int PRIME = 59;
+            int result = 1;
+            Object $type = this.getType();
+            result = result * 59 + ($type == null ? 43 : $type.hashCode());
+            Object $detail = this.getDetail();
+            result = result * 59 + ($detail == null ? 43 : $detail.hashCode());
+            return result;
+        }
+
+        public String toString() {
+            return "RuleEngineDetail.CommonVO(type=" + this.getType() + ", detail=" + this.getDetail() + ")";
+        }
+    }
+}

+ 324 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineDeviceVO.java

@@ -0,0 +1,324 @@
+package com.usky.rule.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.io.Serializable;
+
+@ApiModel
+public class RuleEngineDeviceVO implements Serializable {
+    private static final long serialVersionUID = -27408106462252992L;
+    @ApiModelProperty("自增ID")
+    private Long id;
+    @ApiModelProperty("设备ID")
+    private String deviceId;
+    @ApiModelProperty("标识符")
+    private String identifier;
+    @ApiModelProperty("规则引擎ID")
+    private Long ruleEngineId;
+    @ApiModelProperty("产品ID")
+    private Long productId;
+    @ApiModelProperty("更新人")
+    private Long updatedBy;
+    @ApiModelProperty("记录更新时间")
+    private String updateTime;
+    @ApiModelProperty("创建人")
+    private Long createdBy;
+    @ApiModelProperty("记录创建时间")
+    private String createTime;
+
+    public static RuleEngineDeviceVOBuilder builder() {
+        return new RuleEngineDeviceVOBuilder();
+    }
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public String getDeviceId() {
+        return this.deviceId;
+    }
+
+    public String getIdentifier() {
+        return this.identifier;
+    }
+
+    public Long getRuleEngineId() {
+        return this.ruleEngineId;
+    }
+
+    public Long getProductId() {
+        return this.productId;
+    }
+
+    public Long getUpdatedBy() {
+        return this.updatedBy;
+    }
+
+    public String getUpdateTime() {
+        return this.updateTime;
+    }
+
+    public Long getCreatedBy() {
+        return this.createdBy;
+    }
+
+    public String getCreateTime() {
+        return this.createTime;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public void setDeviceId(final String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public void setIdentifier(final String identifier) {
+        this.identifier = identifier;
+    }
+
+    public void setRuleEngineId(final Long ruleEngineId) {
+        this.ruleEngineId = ruleEngineId;
+    }
+
+    public void setProductId(final Long productId) {
+        this.productId = productId;
+    }
+
+    public void setUpdatedBy(final Long updatedBy) {
+        this.updatedBy = updatedBy;
+    }
+
+    public void setUpdateTime(final String updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public void setCreatedBy(final Long createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public void setCreateTime(final String createTime) {
+        this.createTime = createTime;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof RuleEngineDeviceVO)) {
+            return false;
+        } else {
+            RuleEngineDeviceVO other = (RuleEngineDeviceVO)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$id = this.getId();
+                Object other$id = other.getId();
+                if (this$id == null) {
+                    if (other$id != null) {
+                        return false;
+                    }
+                } else if (!this$id.equals(other$id)) {
+                    return false;
+                }
+
+                Object this$ruleEngineId = this.getRuleEngineId();
+                Object other$ruleEngineId = other.getRuleEngineId();
+                if (this$ruleEngineId == null) {
+                    if (other$ruleEngineId != null) {
+                        return false;
+                    }
+                } else if (!this$ruleEngineId.equals(other$ruleEngineId)) {
+                    return false;
+                }
+
+                Object this$productId = this.getProductId();
+                Object other$productId = other.getProductId();
+                if (this$productId == null) {
+                    if (other$productId != null) {
+                        return false;
+                    }
+                } else if (!this$productId.equals(other$productId)) {
+                    return false;
+                }
+
+                Object this$updatedBy = this.getUpdatedBy();
+                Object other$updatedBy = other.getUpdatedBy();
+                if (this$updatedBy == null) {
+                    if (other$updatedBy != null) {
+                        return false;
+                    }
+                } else if (!this$updatedBy.equals(other$updatedBy)) {
+                    return false;
+                }
+
+                Object this$createdBy = this.getCreatedBy();
+                Object other$createdBy = other.getCreatedBy();
+                if (this$createdBy == null) {
+                    if (other$createdBy != null) {
+                        return false;
+                    }
+                } else if (!this$createdBy.equals(other$createdBy)) {
+                    return false;
+                }
+
+                Object this$deviceId = this.getDeviceId();
+                Object other$deviceId = other.getDeviceId();
+                if (this$deviceId == null) {
+                    if (other$deviceId != null) {
+                        return false;
+                    }
+                } else if (!this$deviceId.equals(other$deviceId)) {
+                    return false;
+                }
+
+                Object this$identifier = this.getIdentifier();
+                Object other$identifier = other.getIdentifier();
+                if (this$identifier == null) {
+                    if (other$identifier != null) {
+                        return false;
+                    }
+                } else if (!this$identifier.equals(other$identifier)) {
+                    return false;
+                }
+
+                Object this$updateTime = this.getUpdateTime();
+                Object other$updateTime = other.getUpdateTime();
+                if (this$updateTime == null) {
+                    if (other$updateTime != null) {
+                        return false;
+                    }
+                } else if (!this$updateTime.equals(other$updateTime)) {
+                    return false;
+                }
+
+                Object this$createTime = this.getCreateTime();
+                Object other$createTime = other.getCreateTime();
+                if (this$createTime == null) {
+                    if (other$createTime != null) {
+                        return false;
+                    }
+                } else if (!this$createTime.equals(other$createTime)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof RuleEngineDeviceVO;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $id = this.getId();
+        result = result * 59 + ($id == null ? 43 : $id.hashCode());
+        Object $ruleEngineId = this.getRuleEngineId();
+        result = result * 59 + ($ruleEngineId == null ? 43 : $ruleEngineId.hashCode());
+        Object $productId = this.getProductId();
+        result = result * 59 + ($productId == null ? 43 : $productId.hashCode());
+        Object $updatedBy = this.getUpdatedBy();
+        result = result * 59 + ($updatedBy == null ? 43 : $updatedBy.hashCode());
+        Object $createdBy = this.getCreatedBy();
+        result = result * 59 + ($createdBy == null ? 43 : $createdBy.hashCode());
+        Object $deviceId = this.getDeviceId();
+        result = result * 59 + ($deviceId == null ? 43 : $deviceId.hashCode());
+        Object $identifier = this.getIdentifier();
+        result = result * 59 + ($identifier == null ? 43 : $identifier.hashCode());
+        Object $updateTime = this.getUpdateTime();
+        result = result * 59 + ($updateTime == null ? 43 : $updateTime.hashCode());
+        Object $createTime = this.getCreateTime();
+        result = result * 59 + ($createTime == null ? 43 : $createTime.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "RuleEngineDeviceVO(id=" + this.getId() + ", deviceId=" + this.getDeviceId() + ", identifier=" + this.getIdentifier() + ", ruleEngineId=" + this.getRuleEngineId() + ", productId=" + this.getProductId() + ", updatedBy=" + this.getUpdatedBy() + ", updateTime=" + this.getUpdateTime() + ", createdBy=" + this.getCreatedBy() + ", createTime=" + this.getCreateTime() + ")";
+    }
+
+    public RuleEngineDeviceVO(final Long id, final String deviceId, final String identifier, final Long ruleEngineId, final Long productId, final Long updatedBy, final String updateTime, final Long createdBy, final String createTime) {
+        this.id = id;
+        this.deviceId = deviceId;
+        this.identifier = identifier;
+        this.ruleEngineId = ruleEngineId;
+        this.productId = productId;
+        this.updatedBy = updatedBy;
+        this.updateTime = updateTime;
+        this.createdBy = createdBy;
+        this.createTime = createTime;
+    }
+
+    public RuleEngineDeviceVO() {
+    }
+
+    public static class RuleEngineDeviceVOBuilder {
+        private Long id;
+        private String deviceId;
+        private String identifier;
+        private Long ruleEngineId;
+        private Long productId;
+        private Long updatedBy;
+        private String updateTime;
+        private Long createdBy;
+        private String createTime;
+
+        RuleEngineDeviceVOBuilder() {
+        }
+
+        public RuleEngineDeviceVOBuilder id(final Long id) {
+            this.id = id;
+            return this;
+        }
+
+        public RuleEngineDeviceVOBuilder deviceId(final String deviceId) {
+            this.deviceId = deviceId;
+            return this;
+        }
+
+        public RuleEngineDeviceVOBuilder identifier(final String identifier) {
+            this.identifier = identifier;
+            return this;
+        }
+
+        public RuleEngineDeviceVOBuilder ruleEngineId(final Long ruleEngineId) {
+            this.ruleEngineId = ruleEngineId;
+            return this;
+        }
+
+        public RuleEngineDeviceVOBuilder productId(final Long productId) {
+            this.productId = productId;
+            return this;
+        }
+
+        public RuleEngineDeviceVOBuilder updatedBy(final Long updatedBy) {
+            this.updatedBy = updatedBy;
+            return this;
+        }
+
+        public RuleEngineDeviceVOBuilder updateTime(final String updateTime) {
+            this.updateTime = updateTime;
+            return this;
+        }
+
+        public RuleEngineDeviceVOBuilder createdBy(final Long createdBy) {
+            this.createdBy = createdBy;
+            return this;
+        }
+
+        public RuleEngineDeviceVOBuilder createTime(final String createTime) {
+            this.createTime = createTime;
+            return this;
+        }
+
+        public RuleEngineDeviceVO build() {
+            return new RuleEngineDeviceVO(this.id, this.deviceId, this.identifier, this.ruleEngineId, this.productId, this.updatedBy, this.updateTime, this.createdBy, this.createTime);
+        }
+
+        public String toString() {
+            return "RuleEngineDeviceVO.RuleEngineDeviceVOBuilder(id=" + this.id + ", deviceId=" + this.deviceId + ", identifier=" + this.identifier + ", ruleEngineId=" + this.ruleEngineId + ", productId=" + this.productId + ", updatedBy=" + this.updatedBy + ", updateTime=" + this.updateTime + ", createdBy=" + this.createdBy + ", createTime=" + this.createTime + ")";
+        }
+    }
+}

+ 29 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineLogPageRequest.java

@@ -0,0 +1,29 @@
+package com.usky.rule.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 规则执行日志分页查询
+ */
+@Data
+public class RuleEngineLogPageRequest {
+    private Integer pageNum = 1;
+    private Integer pageSize = 10;
+    private Long projectId;
+    private Long ruleEngineId;
+    private String ruleEngineName;
+    @ApiModelProperty("触发类型 device/space/cron")
+    private String triggerType;
+    @ApiModelProperty("执行动作 deviceControl/alarmEvent/workOrder")
+    private String action;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date timeBegin;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date timeEnd;
+    /** 租户号,用于多租户隔离 */
+    private Integer tenantId;
+}

+ 480 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineLogVO.java

@@ -0,0 +1,480 @@
+package com.usky.rule.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@ApiModel
+public class RuleEngineLogVO implements Serializable {
+    private static final long serialVersionUID = -90128267903557833L;
+    @ApiModelProperty("自增ID")
+    private Long id;
+    @ApiModelProperty("项目ID")
+    private Long projectId;
+    @ApiModelProperty("规则引擎ID")
+    private Long ruleEngineId;
+    @ApiModelProperty("规则名称")
+    private String ruleEngineName;
+    @ApiModelProperty("自动触发 0:否 1:是")
+    private Byte autoTrigger;
+    @ApiModelProperty("触发类型 device/space/cron")
+    private String triggerType;
+    @ApiModelProperty("执行动作 deviceControl/alarmEvent/workOrder")
+    private String action;
+    @ApiModelProperty("日志数据")
+    private String detail;
+    @ApiModelProperty("日志内容")
+    private String content;
+    @JsonFormat(
+            pattern = "yyyy-MM-dd HH:mm:ss"
+    )
+    @ApiModelProperty("执行时间")
+    private LocalDateTime time;
+    @ApiModelProperty("更新人")
+    private Long updatedBy;
+    @ApiModelProperty("记录更新时间")
+    private String updateTime;
+    @ApiModelProperty("创建人")
+    private Long createdBy;
+    @ApiModelProperty("记录创建时间")
+    private String createTime;
+
+    public static RuleEngineLogVOBuilder builder() {
+        return new RuleEngineLogVOBuilder();
+    }
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public Long getProjectId() {
+        return this.projectId;
+    }
+
+    public Long getRuleEngineId() {
+        return this.ruleEngineId;
+    }
+
+    public String getRuleEngineName() {
+        return this.ruleEngineName;
+    }
+
+    public Byte getAutoTrigger() {
+        return this.autoTrigger;
+    }
+
+    public String getTriggerType() {
+        return this.triggerType;
+    }
+
+    public String getAction() {
+        return this.action;
+    }
+
+    public String getDetail() {
+        return this.detail;
+    }
+
+    public String getContent() {
+        return this.content;
+    }
+
+    public LocalDateTime getTime() {
+        return this.time;
+    }
+
+    public Long getUpdatedBy() {
+        return this.updatedBy;
+    }
+
+    public String getUpdateTime() {
+        return this.updateTime;
+    }
+
+    public Long getCreatedBy() {
+        return this.createdBy;
+    }
+
+    public String getCreateTime() {
+        return this.createTime;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public void setProjectId(final Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public void setRuleEngineId(final Long ruleEngineId) {
+        this.ruleEngineId = ruleEngineId;
+    }
+
+    public void setRuleEngineName(final String ruleEngineName) {
+        this.ruleEngineName = ruleEngineName;
+    }
+
+    public void setAutoTrigger(final Byte autoTrigger) {
+        this.autoTrigger = autoTrigger;
+    }
+
+    public void setTriggerType(final String triggerType) {
+        this.triggerType = triggerType;
+    }
+
+    public void setAction(final String action) {
+        this.action = action;
+    }
+
+    public void setDetail(final String detail) {
+        this.detail = detail;
+    }
+
+    public void setContent(final String content) {
+        this.content = content;
+    }
+
+    @JsonFormat(
+            pattern = "yyyy-MM-dd HH:mm:ss"
+    )
+    public void setTime(final LocalDateTime time) {
+        this.time = time;
+    }
+
+    public void setUpdatedBy(final Long updatedBy) {
+        this.updatedBy = updatedBy;
+    }
+
+    public void setUpdateTime(final String updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public void setCreatedBy(final Long createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public void setCreateTime(final String createTime) {
+        this.createTime = createTime;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof RuleEngineLogVO)) {
+            return false;
+        } else {
+            RuleEngineLogVO other = (RuleEngineLogVO)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$id = this.getId();
+                Object other$id = other.getId();
+                if (this$id == null) {
+                    if (other$id != null) {
+                        return false;
+                    }
+                } else if (!this$id.equals(other$id)) {
+                    return false;
+                }
+
+                Object this$projectId = this.getProjectId();
+                Object other$projectId = other.getProjectId();
+                if (this$projectId == null) {
+                    if (other$projectId != null) {
+                        return false;
+                    }
+                } else if (!this$projectId.equals(other$projectId)) {
+                    return false;
+                }
+
+                Object this$ruleEngineId = this.getRuleEngineId();
+                Object other$ruleEngineId = other.getRuleEngineId();
+                if (this$ruleEngineId == null) {
+                    if (other$ruleEngineId != null) {
+                        return false;
+                    }
+                } else if (!this$ruleEngineId.equals(other$ruleEngineId)) {
+                    return false;
+                }
+
+                Object this$autoTrigger = this.getAutoTrigger();
+                Object other$autoTrigger = other.getAutoTrigger();
+                if (this$autoTrigger == null) {
+                    if (other$autoTrigger != null) {
+                        return false;
+                    }
+                } else if (!this$autoTrigger.equals(other$autoTrigger)) {
+                    return false;
+                }
+
+                Object this$updatedBy = this.getUpdatedBy();
+                Object other$updatedBy = other.getUpdatedBy();
+                if (this$updatedBy == null) {
+                    if (other$updatedBy != null) {
+                        return false;
+                    }
+                } else if (!this$updatedBy.equals(other$updatedBy)) {
+                    return false;
+                }
+
+                Object this$createdBy = this.getCreatedBy();
+                Object other$createdBy = other.getCreatedBy();
+                if (this$createdBy == null) {
+                    if (other$createdBy != null) {
+                        return false;
+                    }
+                } else if (!this$createdBy.equals(other$createdBy)) {
+                    return false;
+                }
+
+                Object this$ruleEngineName = this.getRuleEngineName();
+                Object other$ruleEngineName = other.getRuleEngineName();
+                if (this$ruleEngineName == null) {
+                    if (other$ruleEngineName != null) {
+                        return false;
+                    }
+                } else if (!this$ruleEngineName.equals(other$ruleEngineName)) {
+                    return false;
+                }
+
+                Object this$triggerType = this.getTriggerType();
+                Object other$triggerType = other.getTriggerType();
+                if (this$triggerType == null) {
+                    if (other$triggerType != null) {
+                        return false;
+                    }
+                } else if (!this$triggerType.equals(other$triggerType)) {
+                    return false;
+                }
+
+                Object this$action = this.getAction();
+                Object other$action = other.getAction();
+                if (this$action == null) {
+                    if (other$action != null) {
+                        return false;
+                    }
+                } else if (!this$action.equals(other$action)) {
+                    return false;
+                }
+
+                Object this$detail = this.getDetail();
+                Object other$detail = other.getDetail();
+                if (this$detail == null) {
+                    if (other$detail != null) {
+                        return false;
+                    }
+                } else if (!this$detail.equals(other$detail)) {
+                    return false;
+                }
+
+                Object this$content = this.getContent();
+                Object other$content = other.getContent();
+                if (this$content == null) {
+                    if (other$content != null) {
+                        return false;
+                    }
+                } else if (!this$content.equals(other$content)) {
+                    return false;
+                }
+
+                Object this$time = this.getTime();
+                Object other$time = other.getTime();
+                if (this$time == null) {
+                    if (other$time != null) {
+                        return false;
+                    }
+                } else if (!this$time.equals(other$time)) {
+                    return false;
+                }
+
+                Object this$updateTime = this.getUpdateTime();
+                Object other$updateTime = other.getUpdateTime();
+                if (this$updateTime == null) {
+                    if (other$updateTime != null) {
+                        return false;
+                    }
+                } else if (!this$updateTime.equals(other$updateTime)) {
+                    return false;
+                }
+
+                Object this$createTime = this.getCreateTime();
+                Object other$createTime = other.getCreateTime();
+                if (this$createTime == null) {
+                    if (other$createTime != null) {
+                        return false;
+                    }
+                } else if (!this$createTime.equals(other$createTime)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof RuleEngineLogVO;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $id = this.getId();
+        result = result * 59 + ($id == null ? 43 : $id.hashCode());
+        Object $projectId = this.getProjectId();
+        result = result * 59 + ($projectId == null ? 43 : $projectId.hashCode());
+        Object $ruleEngineId = this.getRuleEngineId();
+        result = result * 59 + ($ruleEngineId == null ? 43 : $ruleEngineId.hashCode());
+        Object $autoTrigger = this.getAutoTrigger();
+        result = result * 59 + ($autoTrigger == null ? 43 : $autoTrigger.hashCode());
+        Object $updatedBy = this.getUpdatedBy();
+        result = result * 59 + ($updatedBy == null ? 43 : $updatedBy.hashCode());
+        Object $createdBy = this.getCreatedBy();
+        result = result * 59 + ($createdBy == null ? 43 : $createdBy.hashCode());
+        Object $ruleEngineName = this.getRuleEngineName();
+        result = result * 59 + ($ruleEngineName == null ? 43 : $ruleEngineName.hashCode());
+        Object $triggerType = this.getTriggerType();
+        result = result * 59 + ($triggerType == null ? 43 : $triggerType.hashCode());
+        Object $action = this.getAction();
+        result = result * 59 + ($action == null ? 43 : $action.hashCode());
+        Object $detail = this.getDetail();
+        result = result * 59 + ($detail == null ? 43 : $detail.hashCode());
+        Object $content = this.getContent();
+        result = result * 59 + ($content == null ? 43 : $content.hashCode());
+        Object $time = this.getTime();
+        result = result * 59 + ($time == null ? 43 : $time.hashCode());
+        Object $updateTime = this.getUpdateTime();
+        result = result * 59 + ($updateTime == null ? 43 : $updateTime.hashCode());
+        Object $createTime = this.getCreateTime();
+        result = result * 59 + ($createTime == null ? 43 : $createTime.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "RuleEngineLogVO(id=" + this.getId() + ", projectId=" + this.getProjectId() + ", ruleEngineId=" + this.getRuleEngineId() + ", ruleEngineName=" + this.getRuleEngineName() + ", autoTrigger=" + this.getAutoTrigger() + ", triggerType=" + this.getTriggerType() + ", action=" + this.getAction() + ", detail=" + this.getDetail() + ", content=" + this.getContent() + ", time=" + this.getTime() + ", updatedBy=" + this.getUpdatedBy() + ", updateTime=" + this.getUpdateTime() + ", createdBy=" + this.getCreatedBy() + ", createTime=" + this.getCreateTime() + ")";
+    }
+
+    public RuleEngineLogVO(final Long id, final Long projectId, final Long ruleEngineId, final String ruleEngineName, final Byte autoTrigger, final String triggerType, final String action, final String detail, final String content, final LocalDateTime time, final Long updatedBy, final String updateTime, final Long createdBy, final String createTime) {
+        this.id = id;
+        this.projectId = projectId;
+        this.ruleEngineId = ruleEngineId;
+        this.ruleEngineName = ruleEngineName;
+        this.autoTrigger = autoTrigger;
+        this.triggerType = triggerType;
+        this.action = action;
+        this.detail = detail;
+        this.content = content;
+        this.time = time;
+        this.updatedBy = updatedBy;
+        this.updateTime = updateTime;
+        this.createdBy = createdBy;
+        this.createTime = createTime;
+    }
+
+    public RuleEngineLogVO() {
+    }
+
+    public static class RuleEngineLogVOBuilder {
+        private Long id;
+        private Long projectId;
+        private Long ruleEngineId;
+        private String ruleEngineName;
+        private Byte autoTrigger;
+        private String triggerType;
+        private String action;
+        private String detail;
+        private String content;
+        private LocalDateTime time;
+        private Long updatedBy;
+        private String updateTime;
+        private Long createdBy;
+        private String createTime;
+
+        RuleEngineLogVOBuilder() {
+        }
+
+        public RuleEngineLogVOBuilder id(final Long id) {
+            this.id = id;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder projectId(final Long projectId) {
+            this.projectId = projectId;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder ruleEngineId(final Long ruleEngineId) {
+            this.ruleEngineId = ruleEngineId;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder ruleEngineName(final String ruleEngineName) {
+            this.ruleEngineName = ruleEngineName;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder autoTrigger(final Byte autoTrigger) {
+            this.autoTrigger = autoTrigger;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder triggerType(final String triggerType) {
+            this.triggerType = triggerType;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder action(final String action) {
+            this.action = action;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder detail(final String detail) {
+            this.detail = detail;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder content(final String content) {
+            this.content = content;
+            return this;
+        }
+
+        @JsonFormat(
+                pattern = "yyyy-MM-dd HH:mm:ss"
+        )
+        public RuleEngineLogVOBuilder time(final LocalDateTime time) {
+            this.time = time;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder updatedBy(final Long updatedBy) {
+            this.updatedBy = updatedBy;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder updateTime(final String updateTime) {
+            this.updateTime = updateTime;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder createdBy(final Long createdBy) {
+            this.createdBy = createdBy;
+            return this;
+        }
+
+        public RuleEngineLogVOBuilder createTime(final String createTime) {
+            this.createTime = createTime;
+            return this;
+        }
+
+        public RuleEngineLogVO build() {
+            return new RuleEngineLogVO(this.id, this.projectId, this.ruleEngineId, this.ruleEngineName, this.autoTrigger, this.triggerType, this.action, this.detail, this.content, this.time, this.updatedBy, this.updateTime, this.createdBy, this.createTime);
+        }
+
+        public String toString() {
+            return "RuleEngineLogVO.RuleEngineLogVOBuilder(id=" + this.id + ", projectId=" + this.projectId + ", ruleEngineId=" + this.ruleEngineId + ", ruleEngineName=" + this.ruleEngineName + ", autoTrigger=" + this.autoTrigger + ", triggerType=" + this.triggerType + ", action=" + this.action + ", detail=" + this.detail + ", content=" + this.content + ", time=" + this.time + ", updatedBy=" + this.updatedBy + ", updateTime=" + this.updateTime + ", createdBy=" + this.createdBy + ", createTime=" + this.createTime + ")";
+        }
+    }
+}

+ 19 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEnginePageRequest.java

@@ -0,0 +1,19 @@
+package com.usky.rule.vo;
+
+import lombok.Data;
+
+/**
+ * 数据规则引擎分页查询
+ */
+@Data
+public class RuleEnginePageRequest {
+    private Integer pageNum = 1;
+    private Integer pageSize = 10;
+    private Long projectId;
+    private Long spaceId;
+    /** 名称模糊 */
+    private String name;
+    /** 状态 0/1 */
+    private Integer status;
+    private Integer tenantId;
+}

+ 411 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/RuleEngineVO.java

@@ -0,0 +1,411 @@
+package com.usky.rule.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.io.Serializable;
+
+@ApiModel
+public class RuleEngineVO implements Serializable {
+    private static final long serialVersionUID = 212526303386371052L;
+    @ApiModelProperty("自增ID")
+    private Long id;
+    @ApiModelProperty("项目ID")
+    private Long projectId;
+    @ApiModelProperty("名称")
+    private String name;
+    @ApiModelProperty("空间ID")
+    private Long spaceId;
+    @ApiModelProperty("规则状态 1:启用 0:停用")
+    private Short status;
+    @ApiModelProperty("规则描述")
+    private String descr;
+    @ApiModelProperty("规则详情")
+    private String detail;
+    @ApiModelProperty("更新人")
+    private Long updatedBy;
+    @ApiModelProperty("记录更新时间")
+    private String updateTime;
+    @ApiModelProperty("创建人")
+    private Long createdBy;
+    @ApiModelProperty("记录创建时间")
+    private String createTime;
+    @ApiModelProperty("空间名称")
+    private String spaceName;
+
+    public static RuleEngineVOBuilder builder() {
+        return new RuleEngineVOBuilder();
+    }
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public Long getProjectId() {
+        return this.projectId;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public Long getSpaceId() {
+        return this.spaceId;
+    }
+
+    public Short getStatus() {
+        return this.status;
+    }
+
+    public String getDescr() {
+        return this.descr;
+    }
+
+    public String getDetail() {
+        return this.detail;
+    }
+
+    public Long getUpdatedBy() {
+        return this.updatedBy;
+    }
+
+    public String getUpdateTime() {
+        return this.updateTime;
+    }
+
+    public Long getCreatedBy() {
+        return this.createdBy;
+    }
+
+    public String getCreateTime() {
+        return this.createTime;
+    }
+
+    public String getSpaceName() {
+        return this.spaceName;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public void setProjectId(final Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public void setSpaceId(final Long spaceId) {
+        this.spaceId = spaceId;
+    }
+
+    public void setStatus(final Short status) {
+        this.status = status;
+    }
+
+    public void setDescr(final String descr) {
+        this.descr = descr;
+    }
+
+    public void setDetail(final String detail) {
+        this.detail = detail;
+    }
+
+    public void setUpdatedBy(final Long updatedBy) {
+        this.updatedBy = updatedBy;
+    }
+
+    public void setUpdateTime(final String updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public void setCreatedBy(final Long createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public void setCreateTime(final String createTime) {
+        this.createTime = createTime;
+    }
+
+    public void setSpaceName(final String spaceName) {
+        this.spaceName = spaceName;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof RuleEngineVO)) {
+            return false;
+        } else {
+            RuleEngineVO other = (RuleEngineVO)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$id = this.getId();
+                Object other$id = other.getId();
+                if (this$id == null) {
+                    if (other$id != null) {
+                        return false;
+                    }
+                } else if (!this$id.equals(other$id)) {
+                    return false;
+                }
+
+                Object this$projectId = this.getProjectId();
+                Object other$projectId = other.getProjectId();
+                if (this$projectId == null) {
+                    if (other$projectId != null) {
+                        return false;
+                    }
+                } else if (!this$projectId.equals(other$projectId)) {
+                    return false;
+                }
+
+                Object this$spaceId = this.getSpaceId();
+                Object other$spaceId = other.getSpaceId();
+                if (this$spaceId == null) {
+                    if (other$spaceId != null) {
+                        return false;
+                    }
+                } else if (!this$spaceId.equals(other$spaceId)) {
+                    return false;
+                }
+
+                Object this$status = this.getStatus();
+                Object other$status = other.getStatus();
+                if (this$status == null) {
+                    if (other$status != null) {
+                        return false;
+                    }
+                } else if (!this$status.equals(other$status)) {
+                    return false;
+                }
+
+                Object this$updatedBy = this.getUpdatedBy();
+                Object other$updatedBy = other.getUpdatedBy();
+                if (this$updatedBy == null) {
+                    if (other$updatedBy != null) {
+                        return false;
+                    }
+                } else if (!this$updatedBy.equals(other$updatedBy)) {
+                    return false;
+                }
+
+                Object this$createdBy = this.getCreatedBy();
+                Object other$createdBy = other.getCreatedBy();
+                if (this$createdBy == null) {
+                    if (other$createdBy != null) {
+                        return false;
+                    }
+                } else if (!this$createdBy.equals(other$createdBy)) {
+                    return false;
+                }
+
+                Object this$name = this.getName();
+                Object other$name = other.getName();
+                if (this$name == null) {
+                    if (other$name != null) {
+                        return false;
+                    }
+                } else if (!this$name.equals(other$name)) {
+                    return false;
+                }
+
+                Object this$descr = this.getDescr();
+                Object other$descr = other.getDescr();
+                if (this$descr == null) {
+                    if (other$descr != null) {
+                        return false;
+                    }
+                } else if (!this$descr.equals(other$descr)) {
+                    return false;
+                }
+
+                Object this$detail = this.getDetail();
+                Object other$detail = other.getDetail();
+                if (this$detail == null) {
+                    if (other$detail != null) {
+                        return false;
+                    }
+                } else if (!this$detail.equals(other$detail)) {
+                    return false;
+                }
+
+                Object this$updateTime = this.getUpdateTime();
+                Object other$updateTime = other.getUpdateTime();
+                if (this$updateTime == null) {
+                    if (other$updateTime != null) {
+                        return false;
+                    }
+                } else if (!this$updateTime.equals(other$updateTime)) {
+                    return false;
+                }
+
+                Object this$createTime = this.getCreateTime();
+                Object other$createTime = other.getCreateTime();
+                if (this$createTime == null) {
+                    if (other$createTime != null) {
+                        return false;
+                    }
+                } else if (!this$createTime.equals(other$createTime)) {
+                    return false;
+                }
+
+                Object this$spaceName = this.getSpaceName();
+                Object other$spaceName = other.getSpaceName();
+                if (this$spaceName == null) {
+                    if (other$spaceName != null) {
+                        return false;
+                    }
+                } else if (!this$spaceName.equals(other$spaceName)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof RuleEngineVO;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $id = this.getId();
+        result = result * 59 + ($id == null ? 43 : $id.hashCode());
+        Object $projectId = this.getProjectId();
+        result = result * 59 + ($projectId == null ? 43 : $projectId.hashCode());
+        Object $spaceId = this.getSpaceId();
+        result = result * 59 + ($spaceId == null ? 43 : $spaceId.hashCode());
+        Object $status = this.getStatus();
+        result = result * 59 + ($status == null ? 43 : $status.hashCode());
+        Object $updatedBy = this.getUpdatedBy();
+        result = result * 59 + ($updatedBy == null ? 43 : $updatedBy.hashCode());
+        Object $createdBy = this.getCreatedBy();
+        result = result * 59 + ($createdBy == null ? 43 : $createdBy.hashCode());
+        Object $name = this.getName();
+        result = result * 59 + ($name == null ? 43 : $name.hashCode());
+        Object $descr = this.getDescr();
+        result = result * 59 + ($descr == null ? 43 : $descr.hashCode());
+        Object $detail = this.getDetail();
+        result = result * 59 + ($detail == null ? 43 : $detail.hashCode());
+        Object $updateTime = this.getUpdateTime();
+        result = result * 59 + ($updateTime == null ? 43 : $updateTime.hashCode());
+        Object $createTime = this.getCreateTime();
+        result = result * 59 + ($createTime == null ? 43 : $createTime.hashCode());
+        Object $spaceName = this.getSpaceName();
+        result = result * 59 + ($spaceName == null ? 43 : $spaceName.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "RuleEngineVO(id=" + this.getId() + ", projectId=" + this.getProjectId() + ", name=" + this.getName() + ", spaceId=" + this.getSpaceId() + ", status=" + this.getStatus() + ", descr=" + this.getDescr() + ", detail=" + this.getDetail() + ", updatedBy=" + this.getUpdatedBy() + ", updateTime=" + this.getUpdateTime() + ", createdBy=" + this.getCreatedBy() + ", createTime=" + this.getCreateTime() + ", spaceName=" + this.getSpaceName() + ")";
+    }
+
+    public RuleEngineVO(final Long id, final Long projectId, final String name, final Long spaceId, final Short status, final String descr, final String detail, final Long updatedBy, final String updateTime, final Long createdBy, final String createTime, final String spaceName) {
+        this.id = id;
+        this.projectId = projectId;
+        this.name = name;
+        this.spaceId = spaceId;
+        this.status = status;
+        this.descr = descr;
+        this.detail = detail;
+        this.updatedBy = updatedBy;
+        this.updateTime = updateTime;
+        this.createdBy = createdBy;
+        this.createTime = createTime;
+        this.spaceName = spaceName;
+    }
+
+    public RuleEngineVO() {
+    }
+
+    public static class RuleEngineVOBuilder {
+        private Long id;
+        private Long projectId;
+        private String name;
+        private Long spaceId;
+        private Short status;
+        private String descr;
+        private String detail;
+        private Long updatedBy;
+        private String updateTime;
+        private Long createdBy;
+        private String createTime;
+        private String spaceName;
+
+        RuleEngineVOBuilder() {
+        }
+
+        public RuleEngineVOBuilder id(final Long id) {
+            this.id = id;
+            return this;
+        }
+
+        public RuleEngineVOBuilder projectId(final Long projectId) {
+            this.projectId = projectId;
+            return this;
+        }
+
+        public RuleEngineVOBuilder name(final String name) {
+            this.name = name;
+            return this;
+        }
+
+        public RuleEngineVOBuilder spaceId(final Long spaceId) {
+            this.spaceId = spaceId;
+            return this;
+        }
+
+        public RuleEngineVOBuilder status(final Short status) {
+            this.status = status;
+            return this;
+        }
+
+        public RuleEngineVOBuilder descr(final String descr) {
+            this.descr = descr;
+            return this;
+        }
+
+        public RuleEngineVOBuilder detail(final String detail) {
+            this.detail = detail;
+            return this;
+        }
+
+        public RuleEngineVOBuilder updatedBy(final Long updatedBy) {
+            this.updatedBy = updatedBy;
+            return this;
+        }
+
+        public RuleEngineVOBuilder updateTime(final String updateTime) {
+            this.updateTime = updateTime;
+            return this;
+        }
+
+        public RuleEngineVOBuilder createdBy(final Long createdBy) {
+            this.createdBy = createdBy;
+            return this;
+        }
+
+        public RuleEngineVOBuilder createTime(final String createTime) {
+            this.createTime = createTime;
+            return this;
+        }
+
+        public RuleEngineVOBuilder spaceName(final String spaceName) {
+            this.spaceName = spaceName;
+            return this;
+        }
+
+        public RuleEngineVO build() {
+            return new RuleEngineVO(this.id, this.projectId, this.name, this.spaceId, this.status, this.descr, this.detail, this.updatedBy, this.updateTime, this.createdBy, this.createTime, this.spaceName);
+        }
+
+        public String toString() {
+            return "RuleEngineVO.RuleEngineVOBuilder(id=" + this.id + ", projectId=" + this.projectId + ", name=" + this.name + ", spaceId=" + this.spaceId + ", status=" + this.status + ", descr=" + this.descr + ", detail=" + this.detail + ", updatedBy=" + this.updatedBy + ", updateTime=" + this.updateTime + ", createdBy=" + this.createdBy + ", createTime=" + this.createTime + ", spaceName=" + this.spaceName + ")";
+        }
+    }
+}

+ 107 - 0
service-rule/service-rule-biz/src/main/java/com/usky/rule/vo/TimeRange.java

@@ -0,0 +1,107 @@
+package com.usky.rule.vo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class TimeRange {
+    @JsonProperty("type")
+    private String type;
+    @JsonProperty("start")
+    private Integer start;
+    @JsonProperty("end")
+    private Integer end;
+
+    public TimeRange() {
+    }
+
+    public String getType() {
+        return this.type;
+    }
+
+    public Integer getStart() {
+        return this.start;
+    }
+
+    public Integer getEnd() {
+        return this.end;
+    }
+
+    @JsonProperty("type")
+    public void setType(final String type) {
+        this.type = type;
+    }
+
+    @JsonProperty("start")
+    public void setStart(final Integer start) {
+        this.start = start;
+    }
+
+    @JsonProperty("end")
+    public void setEnd(final Integer end) {
+        this.end = end;
+    }
+
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        } else if (!(o instanceof TimeRange)) {
+            return false;
+        } else {
+            TimeRange other = (TimeRange)o;
+            if (!other.canEqual(this)) {
+                return false;
+            } else {
+                Object this$start = this.getStart();
+                Object other$start = other.getStart();
+                if (this$start == null) {
+                    if (other$start != null) {
+                        return false;
+                    }
+                } else if (!this$start.equals(other$start)) {
+                    return false;
+                }
+
+                Object this$end = this.getEnd();
+                Object other$end = other.getEnd();
+                if (this$end == null) {
+                    if (other$end != null) {
+                        return false;
+                    }
+                } else if (!this$end.equals(other$end)) {
+                    return false;
+                }
+
+                Object this$type = this.getType();
+                Object other$type = other.getType();
+                if (this$type == null) {
+                    if (other$type != null) {
+                        return false;
+                    }
+                } else if (!this$type.equals(other$type)) {
+                    return false;
+                }
+
+                return true;
+            }
+        }
+    }
+
+    protected boolean canEqual(final Object other) {
+        return other instanceof TimeRange;
+    }
+
+    public int hashCode() {
+        int PRIME = 59;
+        int result = 1;
+        Object $start = this.getStart();
+        result = result * 59 + ($start == null ? 43 : $start.hashCode());
+        Object $end = this.getEnd();
+        result = result * 59 + ($end == null ? 43 : $end.hashCode());
+        Object $type = this.getType();
+        result = result * 59 + ($type == null ? 43 : $type.hashCode());
+        return result;
+    }
+
+    public String toString() {
+        return "TimeRange(type=" + this.getType() + ", start=" + this.getStart() + ", end=" + this.getEnd() + ")";
+    }
+}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio