修改总结-多联动规则支持.md 12 KB

多联动规则支持 - 修改总结

一、修改概述

与定时规则类似,现在支持一个任务绑定多个传感器联动规则:

  • ✅ 一个任务可以同时配置多个联动规则
  • ✅ 每个规则可以绑定不同的传感器设备
  • ✅ 同一传感器可以配置多个不同阈值条件的规则
  • ✅ 每个规则可独立启用/禁用
  • ✅ 规则之间互不干扰,独立触发

二、修改内容

1. 数据库层

现有表: wfauto_v2_link_rule

联动规则表已经存在,支持多规则设计:

  • 通过 task_id 字段关联任务(一对多关系)
  • 每个规则有独立的 idrule_namerule_code
  • 支持冷却时间机制(cooldown_minutes)
  • 记录触发次数(trigger_count)和最后触发时间(last_trigger_at)

2. 实体类

2.1 LinkageRule.java (已存在)

已具备多规则支持的完整结构:

  • 包含所有联动规则相关字段
  • 提供业务方法:matchCondition(), getCooldownSeconds(), incrementTriggerCount()
  • 支持多种比较运算符:> / >= / < / <= / ==

3. 数据访问层

3.1 LinkageRuleMapper.java (已修改)

新增方法:

/**
 * 根据任务ID逻辑删除所有规则
 */
int deleteByTaskId(@Param("taskId") Long taskId);

已有方法:

  • insert(LinkageRule) - 插入规则
  • selectByTaskId(Long) - 查询任务的所有规则
  • selectEnabledBySensorId(String) - 查询传感器的启用规则
  • selectBySensorId(String) - 查询传感器的所有规则
  • updateById(LinkageRule) - 更新规则
  • deleteById(Long) - 删除单个规则

4. API层修改

4.1 CreateTaskRequest DTO

新增字段:

/**
 * 传感器联动规则列表(可选,为空则不配置联动触发)
 * 支持配置多个联动规则,可以绑定不同传感器或同一传感器的不同阈值条件
 */
private List<LinkageRuleDTO> linkageRules;

@Data
public static class LinkageRuleDTO {
    @NotBlank(message = "规则名称不能为空")
    private String ruleName;

    private String ruleCode;

    @NotBlank(message = "传感器设备ID不能为空")
    private String sensorDeviceId;

    @NotBlank(message = "传感器数据项编码不能为空")
    private String dataItemCode;  // 如: temperature, humidity, soilMoisture

    @NotBlank(message = "比较运算符不能为空")
    private String operator;  // GT(>), GTE(>=), LT(<), LTE(<=), EQ(=), NEQ(!=)

    @NotNull(message = "阈值不能为空")
    private Double threshold;

    private Integer cooldownMinutes;  // 默认30分钟

    private Boolean enabled;  // 默认true
}

5. 业务逻辑层修改

5.1 IrrigationTaskController.java

修改的方法:

  1. createTask() - 支持创建多个联动规则

    // 5. 保存联动规则(如果配置了)
    if (request.getLinkageRules() != null && !request.getLinkageRules().isEmpty()) {
       for (LinkageRuleDTO linkageDTO : request.getLinkageRules()) {
           LinkageRule linkageRule = LinkageRule.builder()
               .taskId(taskId)
               .ruleName(linkageDTO.getRuleName())
               .ruleCode(linkageDTO.getRuleCode())
               .sensorDeviceId(linkageDTO.getSensorDeviceId())
               .sensorDataType(linkageDTO.getDataItemCode())
               .operator(linkageDTO.getOperator())
               .threshold(BigDecimal.valueOf(linkageDTO.getThreshold()))
               .cooldownMinutes(linkageDTO.getCooldownMinutes() != null ?
                   linkageDTO.getCooldownMinutes() : 30)
               .enabled(linkageDTO.getEnabled() != null ?
                   linkageDTO.getEnabled() : true)
               .triggerCount(0)
               .tenantId(request.getTenantId())
               .createdAt(LocalDateTime.now())
               .deleted(false)
               .build();
    
           linkageRuleMapper.insert(linkageRule);
       }
    }
    
  2. deleteTask() - 删除所有联动规则

    // 逻辑删除所有联动规则
    linkageRuleMapper.deleteByTaskId(id);
    

三、API 使用示例

3.1 创建任务(多个联动规则)

POST /api/tasks
Content-Type: application/json

{
  "taskName": "农场A区智能轮灌",
  "triggerType": "LINKAGE",
  "linkageRules": [
    {
      "ruleName": "土壤湿度过低触发",
      "ruleCode": "SOIL_MOISTURE_LOW",
      "sensorDeviceId": "sensor-soil-001",
      "dataItemCode": "soilMoisture",
      "operator": "<",
      "threshold": 30.0,
      "cooldownMinutes": 60,
      "enabled": true
    },
    {
      "ruleName": "温度过高触发",
      "ruleCode": "TEMP_HIGH",
      "sensorDeviceId": "sensor-temp-001",
      "dataItemCode": "temperature",
      "operator": ">",
      "threshold": 35.0,
      "cooldownMinutes": 30,
      "enabled": true
    },
    {
      "ruleName": "土壤湿度传感器备用规则",
      "ruleCode": "SOIL_MOISTURE_LOW_BACKUP",
      "sensorDeviceId": "sensor-soil-002",
      "dataItemCode": "soilMoisture",
      "operator": "<=",
      "threshold": 25.0,
      "cooldownMinutes": 60,
      "enabled": true
    }
  ],
  "pumpId": "pump-001",
  "pressureMode": "PUMP_UNIFIED",
  "targetPressureKpa": 300,
  "groupConfigs": [
    {
      "groupId": 101,
      "sortOrder": 1,
      "irrigationDurationMinutes": 30
    }
  ],
  "tenantId": 1
}

3.2 定时+联动混合模式

{
  "taskName": "农场B区混合触发灌溉",
  "triggerType": "SCHEDULED",
  "scheduleRules": [
    {
      "ruleName": "每天凌晨2点",
      "scheduleType": "CRON",
      "cronExpression": "0 0 2 * * ?",
      "enabled": true
    }
  ],
  "linkageRules": [
    {
      "ruleName": "土壤湿度应急补水",
      "sensorDeviceId": "sensor-soil-003",
      "dataItemCode": "soilMoisture",
      "operator": "<",
      "threshold": 20.0,
      "cooldownMinutes": 120,
      "enabled": true
    }
  ],
  "pumpId": "pump-002",
  "pressureMode": "PUMP_ZONE",
  "groupConfigs": [
    {
      "groupId": 201,
      "sortOrder": 1,
      "irrigationDurationMinutes": 25
    }
  ],
  "tenantId": 1
}

3.3 查询任务的联动规则

SELECT * FROM wfauto_v2_link_rule
WHERE task_id = 1 AND deleted = 0
ORDER BY id;

3.4 查询规则触发状态

SELECT
  r.id,
  r.rule_name,
  r.sensor_device_id,
  r.sensor_data_type,
  r.operator,
  r.threshold,
  r.cooldown_minutes,
  r.enabled,
  r.trigger_count,
  r.last_trigger_at,
  TIMESTAMPDIFF(MINUTE, r.last_trigger_at, NOW()) AS minutes_since_last_trigger
FROM wfauto_v2_link_rule r
WHERE r.task_id = 1 AND r.deleted = 0
ORDER BY r.id;

3.5 查询传感器关联的所有规则

SELECT
  r.id,
  r.rule_name,
  r.task_id,
  t.task_name,
  r.operator,
  r.threshold,
  r.enabled
FROM wfauto_v2_link_rule r
LEFT JOIN wfauto_v2_irrigation_task t ON t.id = r.task_id
WHERE r.sensor_device_id = 'sensor-soil-001'
  AND r.deleted = 0
  AND r.enabled = 1;

四、技术优势

4.1 灵活性

  • ✅ 支持同时配置多个传感器的联动规则
  • ✅ 同一传感器可配置多个不同阈值的规则
  • ✅ 每个规则可独立启用/禁用
  • ✅ 规则可动态增删改

4.2 可靠性

  • ✅ 冷却时间机制防止频繁触发
  • ✅ 触发次数统计便于分析规则效率
  • ✅ 多规则冗余设计提高系统容错性

4.3 可维护性

  • ✅ 联动规则独立管理,职责清晰
  • ✅ 符合关系型数据库设计规范
  • ✅ 便于后续扩展(如增加规则优先级、复合条件等)

4.4 性能

  • ✅ 通过传感器ID索引快速查询相关规则
  • ✅ 冷却时间机制减少不必要的任务触发
  • ✅ 规则触发状态独立记录,互不影响

4.5 安全性

  • ✅ 每个规则独立的冷却时间控制
  • ✅ 触发次数限制防止恶意触发
  • ✅ 规则故障不影响其他规则

五、与定时规则的对比

特性 定时规则 联动规则
触发方式 基于时间调度 基于传感器数据
调度引擎 Quartz 事件驱动
防重复机制 Quartz自带 冷却时间
执行次数控制 SIMPLE模式支持 无限制,统计触发次数
状态管理 ACTIVE/COMPLETED/DISABLED enabled字段
独立性 每个规则独立Job 每个规则独立判断
共同点 支持多规则,互不干扰,独立配置 支持多规则,互不干扰,独立配置

六、注意事项

6.1 规则设计

  • ⚠️ 合理设置冷却时间,避免频繁触发导致设备损耗
  • ⚠️ 同一传感器的多个规则应设置不同阈值,避免重复触发
  • ⚠️ 建议为关键传感器配置备用规则,提高可靠性

6.2 触发机制

  • ⚠️ 传感器数据需要通过消息队列或API推送到系统
  • ⚠️ 系统根据 sensorDeviceId 查询启用的规则并匹配条件
  • ⚠️ 冷却时间内的触发请求会被忽略,不会执行任务

6.3 并发控制

  • ⚠️ 多个联动规则同时满足条件时,需要任务级别的并发防护
  • ⚠️ 建议在 TaskTriggerService.linkageTrigger() 中实现分布式锁
  • ⚠️ 避免同一任务因多个规则同时触发导致重复执行

6.4 数据精度

  • ⚠️ 阈值比较使用 BigDecimal 确保精度
  • ⚠️ 相等比较(==)使用0.001误差容忍度
  • ⚠️ 传感器数据应做异常值过滤

6.5 规则数量

  • ⚠️ 单个任务建议不超过10个联动规则
  • ⚠️ 同一传感器建议不超过5个规则
  • ⚠️ 规则过多会增加匹配开销

七、后续优化建议

7.1 功能扩展

  • 支持复合条件(AND/OR逻辑)
  • 支持规则优先级
  • 支持时间窗口限制(仅在特定时间段生效)
  • 支持规则触发历史查询
  • 支持规则有效期设置

7.2 性能优化

  • 规则匹配缓存
  • 传感器数据变化订阅机制
  • 批量规则匹配
  • 规则热数据缓存(Redis)

7.3 智能化

  • 基于历史数据的阈值自适应调整
  • 规则效果分析(触发次数、成功率等)
  • 异常规则自动禁用
  • 规则冲突检测

八、文件清单

8.1 修改文件

  • src/main/java/cn/sciento/farm/automationv2/api/dto/CreateTaskRequest.java - 新增 LinkageRuleDTO
  • src/main/java/cn/sciento/farm/automationv2/api/controller/IrrigationTaskController.java - 支持多联动规则
  • src/main/java/cn/sciento/farm/automationv2/infra/mapper/LinkageRuleMapper.java - 新增 deleteByTaskId 方法

8.2 已存在(无需修改)

  • src/main/java/cn/sciento/farm/automationv2/domain/entity/LinkageRule.java - 已支持多规则设计
  • src/main/java/cn/sciento/farm/automationv2/domain/repository/LinkageRuleRepository.java
  • src/main/java/cn/sciento/farm/automationv2/infra/repository/impl/LinkageRuleRepositoryImpl.java

九、测试建议

9.1 单元测试

  • LinkageRule 实体类业务方法测试(matchCondition等)
  • LinkageRuleMapper 数据访问测试
  • 冷却时间机制测试

9.2 集成测试

  • 创建任务(多个联动规则)测试
  • 删除任务(级联删除规则)测试
  • 规则独立触发测试
  • 冷却时间有效性测试
  • 并发触发防护测试

9.3 端到端测试

  • 多规则同时满足条件场景
  • 同一传感器多个阈值规则
  • 定时+联动混合触发
  • 规则故障不影响其他规则
  • 传感器数据异常处理

十、与传感器服务集成建议

10.1 数据推送方式

// 方式1: 消息队列(推荐)
@KafkaListener(topics = "sensor-data")
public void handleSensorData(SensorDataEvent event) {
    // 查询该传感器的所有启用规则
    List<LinkageRule> rules = linkageRuleMapper
        .selectEnabledBySensorId(event.getSensorDeviceId());

    for (LinkageRule rule : rules) {
        // 检查是否在冷却期
        if (isInCooldown(rule)) continue;

        // 匹配条件
        if (rule.matchCondition(event.getValue())) {
            // 触发任务
            taskTriggerService.linkageTrigger(rule.getTaskId(), rule.getId());
        }
    }
}

// 方式2: HTTP API
@PostMapping("/api/sensors/{sensorId}/data")
public Result<Void> receiveSensorData(
    @PathVariable String sensorId,
    @RequestBody SensorDataDTO data) {
    // 同上逻辑
}

10.2 冷却期检查

private boolean isInCooldown(LinkageRule rule) {
    if (rule.getLastTriggerAt() == null) {
        return false;
    }

    long minutesSinceLastTrigger = ChronoUnit.MINUTES.between(
        rule.getLastTriggerAt(),
        LocalDateTime.now()
    );

    return minutesSinceLastTrigger < rule.getCooldownMinutes();
}

修改完成时间: 2026-03-02 修改人: Claude Sonnet 4.5 版本: v2.0