feat(iot): Wave 4 Round 2 — B6/B7/B18 ActionProvider + 分支执行 + DataRule迁移

B6 ActionProvider SPI + 5 核心动作(alarm/notify/device-ctrl):
- ActionProvider 接口(extends NodeProvider,默认 bridge execute)
- ActionResult record(SUCCESS/FAILURE/SKIP + output + message)
- ActionProviderManager(Spring 自动收集 + fail-fast 重复 type)
- AlarmTriggerAction(调用 IotAlarmRecordApi.triggerAlarm,模板变量解析)
- AlarmClearAction(alarmId 从 config 或 ctx.metadata 解析,幂等)
- NotifyAction(4 通道并发 + 部分失败不阻塞,第一期 stub)
- DeviceServiceInvokeAction(调用 IotDeviceControlApi.invokeService)
- DevicePropertySetAction(第一期 stub,B27 补全 Redis/MySQL)
- IotAlarmRecordApi + DTO(rule 模块→server 跨模块接口)
- IotAlarmRecordApiImpl(server 端 FeignClient 实现,委托 Service)
- 14 单元测试全绿

B7 分支执行逻辑(executeAnyway if/else-if/else):
- BranchConfiguration POJO(branches[] + executeAnyway + BranchCondition)
- BranchExecutor(核心语义:else/executeAnyway/条件异常短路/action 异常隔离)
- BranchNode NodeProvider(ACTION/"branch",内联执行命中 branch actions)
- DagExecutor 最小扩展(ctx.metadata 传递 CompiledRuleChain 供 BranchNode 使用)
- 9 单元测试全绿(含 validate else 位置校验)

B18 DataRule → DAG 自动转换工具:
- DataRuleToChainMapper(v1→v2 映射,6 种 Sink,合并/拆分多 source)
- DataRuleMigrator(dry-run + execute + 幂等映射表)
- DataRuleMigrationController(3 端点:dry-run/execute/mapping)
- 8 单元测试全绿

测试总计:rule 模块 159/159 ✓,server 模块 8/8(B18)✓

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lzh
2026-04-24 10:10:04 +08:00
parent 42466363c7
commit 24c486900a
23 changed files with 2969 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
package com.viewsh.module.iot.api.alarm;
import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.module.iot.api.alarm.dto.IotAlarmClearReqDTO;
import com.viewsh.module.iot.api.alarm.dto.IotAlarmTriggerReqDTO;
import com.viewsh.module.iot.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* IoT 告警记录 API供 rule 模块调用)
* <p>
* 第一期仅暴露规则引擎 AlarmTriggerAction / AlarmClearAction 需要的两个方法;
* 人工确认 / 归档等管理接口保留在 server 模块 Service 本地调用。
*
* @author B6
*/
@FeignClient(name = ApiConstants.NAME)
@Tag(name = "RPC 服务 - IoT 告警记录")
public interface IotAlarmRecordApi {
String PREFIX = ApiConstants.PREFIX + "/alarm/record";
@PostMapping(PREFIX + "/trigger")
@Operation(summary = "触发告警(幂等:同 deviceId+configId 活跃记录存在则 trigger_count++")
CommonResult<Long> triggerAlarm(@Valid @RequestBody IotAlarmTriggerReqDTO reqDTO);
@PostMapping(PREFIX + "/clear")
@Operation(summary = "清除告警(幂等:已清除重复调用返回 success")
CommonResult<Boolean> clearAlarm(@Valid @RequestBody IotAlarmClearReqDTO reqDTO);
}

View File

@@ -0,0 +1,31 @@
package com.viewsh.module.iot.api.alarm.dto;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 告警清除请求 DTO
* <p>
* 幂等:对已清除告警重复调用为 no-opService 层保证)。
*
* @author B6
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class IotAlarmClearReqDTO {
@NotNull(message = "告警记录 ID 不能为空")
private Long alarmId;
/** 操作人(规则引擎触发通常为 "rule-engine:{chainId}" */
private String operator;
/** 备注 */
private String remark;
}

View File

@@ -0,0 +1,55 @@
package com.viewsh.module.iot.api.alarm.dto;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 告警触发请求 DTO规则引擎 AlarmTriggerAction 调用)
* <p>
* 幂等语义:同一 (deviceId, alarmConfigId) 活跃记录已存在 → trigger_count++
* 否则插入新记录。详见 IotAlarmRecordService.triggerAlarm。
*
* @author B6
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class IotAlarmTriggerReqDTO {
@NotNull(message = "设备 ID 不能为空")
private Long deviceId;
@NotNull(message = "告警配置 ID 不能为空")
private Long alarmConfigId;
@NotNull(message = "告警严重度不能为空")
@Min(value = 1, message = "告警严重度最小为 1")
@Max(value = 5, message = "告警严重度最大为 5")
private Integer severity;
/** 告警名称(冗余便于查询) */
private String alarmName;
/** 产品 ID冗余可选 */
private Long productId;
/** 所属子系统 ID */
private Long subsystemId;
/** v2 规则链 ID */
private Long ruleChainId;
/** v1 场景规则 ID灰度期兼容 */
private Long sceneRuleId;
/** 触发详情(模板解析后的 JSON */
private JsonNode details;
}