与定时规则类似,现在支持一个任务绑定多个传感器联动规则:
wfauto_v2_link_rule联动规则表已经存在,支持多规则设计:
task_id 字段关联任务(一对多关系)id、rule_name、rule_codecooldown_minutes)trigger_count)和最后触发时间(last_trigger_at)已具备多规则支持的完整结构:
matchCondition(), getCooldownSeconds(), incrementTriggerCount()新增方法:
/**
* 根据任务ID逻辑删除所有规则
*/
int deleteByTaskId(@Param("taskId") Long taskId);
已有方法:
insert(LinkageRule) - 插入规则selectByTaskId(Long) - 查询任务的所有规则selectEnabledBySensorId(String) - 查询传感器的启用规则selectBySensorId(String) - 查询传感器的所有规则updateById(LinkageRule) - 更新规则deleteById(Long) - 删除单个规则新增字段:
/**
* 传感器联动规则列表(可选,为空则不配置联动触发)
* 支持配置多个联动规则,可以绑定不同传感器或同一传感器的不同阈值条件
*/
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
}
修改的方法:
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);
}
}
deleteTask() - 删除所有联动规则
// 逻辑删除所有联动规则
linkageRuleMapper.deleteByTaskId(id);
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
}
{
"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
}
SELECT * FROM wfauto_v2_link_rule
WHERE task_id = 1 AND deleted = 0
ORDER BY id;
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;
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;
| 特性 | 定时规则 | 联动规则 |
|---|---|---|
| 触发方式 | 基于时间调度 | 基于传感器数据 |
| 调度引擎 | Quartz | 事件驱动 |
| 防重复机制 | Quartz自带 | 冷却时间 |
| 执行次数控制 | SIMPLE模式支持 | 无限制,统计触发次数 |
| 状态管理 | ACTIVE/COMPLETED/DISABLED | enabled字段 |
| 独立性 | 每个规则独立Job | 每个规则独立判断 |
| 共同点 | 支持多规则,互不干扰,独立配置 | 支持多规则,互不干扰,独立配置 |
sensorDeviceId 查询启用的规则并匹配条件TaskTriggerService.linkageTrigger() 中实现分布式锁BigDecimal 确保精度==)使用0.001误差容忍度src/main/java/cn/sciento/farm/automationv2/api/dto/CreateTaskRequest.java - 新增 LinkageRuleDTOsrc/main/java/cn/sciento/farm/automationv2/api/controller/IrrigationTaskController.java - 支持多联动规则src/main/java/cn/sciento/farm/automationv2/infra/mapper/LinkageRuleMapper.java - 新增 deleteByTaskId 方法src/main/java/cn/sciento/farm/automationv2/domain/entity/LinkageRule.java - 已支持多规则设计src/main/java/cn/sciento/farm/automationv2/domain/repository/LinkageRuleRepository.javasrc/main/java/cn/sciento/farm/automationv2/infra/repository/impl/LinkageRuleRepositoryImpl.java// 方式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) {
// 同上逻辑
}
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