# 需求

实现采集数据超过阈值报警

## issue

[实现采集数据超过阈值报警](http://git.huangwc.com/pig/pig-farm-controller/issues/62)

# 方案

1. **架构核心**: 新增一个 **告警领域服务**，作为告警系统的核心大脑，负责告警事件的生命周期管理。
2. **任务分离**:
    * 新增 **阈值告警任务** (分为区域主控和普通设备两种)，仅负责检测数据并将结果报告给领域服务。
    * 新增 **告警通知发送任务**，作为一个独立的、系统预定义的定时任务，负责调用领域服务，获取并发送所有待处理的通知。
3. **计划调度**:
    * 修改现有 "定时全量数据采集" 计划, 更名为 "周期性系统健康检查"。此计划包含固定的 **全量采集任务** 和由用户动态配置的
      **阈值告警任务**。
    * 新增一个独立的 "告警通知发送" 计划，用于定时执行固定的 **告警通知发送任务**。
4. **数据与接口**:
    * 新增独立的告警记录表（建议采用“活跃告警表 + 历史告警超表”的模式）。
    * 新增相应的告警配置管理接口。

## 方案细节

### 架构与职责划分

1. **告警领域服务 (`internal/domain/alarm/`) - 管理器**
    * **职责**: 作为告警系统的核心大脑，负责处理告警事件的完整生命周期。
    * **功能**:
        * 接收来自检测任务的状态报告（包含设备ID、传感器类型、当前是否异常等信息）。
        * 根据报告和数据库中的告警记录，决策是创建新告警、更新为已解决、还是因被忽略而跳过。
        * 管理“手动忽略” (`Ignored`) 状态和忽略到期时间 (`ignored_until`)。
        * 实现可配置的“重复通知”策略（`re_notification_interval`），决定何时对持续存在的告警再次发送通知。
        * 提供接口供 `告警通知发送任务` 调用，以获取所有待处理的通知。

2. **阈值告警任务 (`internal/domain/task/`) - 检测器**
    * **职责**: 职责纯粹，仅负责执行检测并将结果报告给告警领域服务。
    * **逻辑**: 从传感器数据表读取最新数据 -> 与自身配置的阈值进行比对 -> 无论结果如何，都调用 `告警领域服务.ReportStatus()`
      报告当前状态（正常或异常）。
    * **无状态**: 任务本身不关心告警是否已存在或被忽略，它只负责“状态同步”。

3. **告警通知发送任务 (`internal/domain/task/`) - 发送器**
    * **职责**: 作为一个独立的定时任务，解耦通知发送与告警检测。
    * **逻辑**: 调用 `告警领域服务.GetAndProcessPendingNotifications()` -> 获取待发送通知列表 -> 调用 `通知领域服务`
      逐一发送。
    * **优势**: 统一管理定时任务，实现资源控制，提高系统稳定性和可扩展性。

### 计划与任务调度

1. **"周期性系统健康检查" 计划**
    * **任务构成**:
        * **全量数据采集任务 (ExecutionOrder: 1)**: 系统预定义，必须是第一个执行的任务，为后续的告警检测提供最新的数据基础。
        * **阈值告警任务 (ExecutionOrder: 2, 3...)**: 由用户通过API动态配置和管理，`告警配置服务` 负责将其增删改到此计划中。

2. **"告警通知发送" 计划**
    * **任务构成**: 包含一个系统预定义的 `告警通知发送任务`。
    * **调度**: 可配置独立的执行频率（如每分钟一次），与健康检查计划解耦。

3. **系统初始化 (`data_initializer.go`)**
    * **职责**: 只负责创建和维护系统预定义的、固定的计划和任务。
    * **操作**:
        * 确保 "周期性系统健康检查" 计划存在，并包含 `全量数据采集任务`。
        * 确保 "告警通知发送" 计划存在，并包含 `告警通知发送任务`。
    * **注意**: 初始化逻辑 **不会** 也 **不应该** 触及用户动态配置的阈值告警任务。

### 阈值告警任务 (用户可配置的任务类型)

1. **任务类型**: 提供两种可供用户配置的阈值告警任务类型，分别对应 **区域主控** 和 **普通设备** 告警。
2. **参数结构**:
    * **通用参数**: 任务参数将包含 `Thresholds` (阈值) 和 `Operator` (操作符，如 `>` 或 `<`) 字段。
    * **普通设备任务**: 配置包含 `DeviceID`。
    * **区域主控任务**: 配置包含 `AreaControllerID`, `SensorType`, 以及一个 `ExcludeDeviceIDs` (需要排除的设备ID列表)。

### 告警事件与生命周期

1. **告警事件定义**:
    * 区分 **告警规则** (配置的策略) 和 **告警事件** (规则被具体设备触发的实例)。
    * 区域主控下不同设备触发的告警，即使基于同一规则，也应视为独立的 **告警事件**，以便于精确追溯和独立操作。

2. **生命周期管理**:
    * **自动闭环**: 当阈值告警任务报告数据恢复正常时，告警领域服务会自动将对应的 `Active` 告警事件状态更新为 `Resolved`。
    * **手动忽略 (Snooze)**: 用户可通过接口将告警事件状态置为 `Ignored` 并设置 `ignored_until`
      。在此期间，即使数据持续异常，也不会发送通知。忽略到期后若问题仍存在，告警将重新变为 `Active` 并发送通知。
    * **持续告警与重复通知**: 对持续未解决的 `Active` 告警，只保留一条记录。告警领域服务会根据 `re_notification_interval`
      配置的重复通知间隔，决定是否需要再次发送通知。

### 数据库设计考量

1. **冷热分离方案 (推荐)**:
    * **`active_alarms` (活跃告警表)**:
        * **类型**: 标准 PostgreSQL 表。
        * **内容**: 只存放 `Active` 和 `Ignored` 状态的告警。
        * **优势**: 保证高频读写的性能，避免在被压缩的数据上执行更新操作。
    * **`historical_alarms` (历史告警表)**:
        * **类型**: 改造为 **TimescaleDB 超表**。
        * **内容**: 存放 `Resolved` 状态的告警。当告警在 `active_alarms` 中被解决后，记录将移至此表。
        * **优势**: 适合存储海量历史数据，便于分析、统计，并可利用 TimescaleDB 的压缩和数据生命周期管理功能。

2. **表结构字段**:
    * `status`: 枚举类型，包含 `Active`, `Resolved`, `Ignored`。
    * `ignored_until`: `timestamp` 类型，记录忽略截止时间。
    * `last_notified_at`: `timestamp` 类型，记录上次发送通知的时间。

### 阈值告警服务 (领域层)

1. **服务职责**:
    * 负责管理阈值告警 **任务配置** 的增删改查。这些任务配置包含了具体的阈值规则。
    * 负责将用户创建的阈值告警任务动态更新到 "周期性系统健康检查" 计划中。
    * **任务配置引用检查**: 提供自检方法，用于在删除设备或设备模板前，检查它们是否被任何阈值告警任务配置所引用，以防止产生悬空引用。

2. **排除列表计算与联动**:
    * **删除独立任务配置后归属**: 当一个普通设备的独立告警任务配置被删除时，它将自动从其所属区域主控的 `ExcludeDeviceIDs`
      列表中移除，从而回归到区域统一告警策略的管理之下。
    * **设备生命周期管理**: 在对设备进行修改（特别是更换区域主控）或删除时，以及在删除区域主控时，必须同步更新相关的
      `ExcludeDeviceIDs` 列表，同时解决相关告警(当删除时), 以保证数据一致性。
    * **实现**: `DeviceService` 中负责处理设备更新和删除的方法，需要调用本服务提供的“任务配置引用检查”和刷新接口。

### 阈值告警控制器

1. **独立接口**: 提供两组独立的 Web 接口，分别用于管理区域主控和普通设备的阈值告警配置。
    * 区域主控告警配置接口: `/api/v1/alarm/region-config`
    * 普通设备告警配置接口: `/api/v1/alarm/device-config`
2. **接口职责**: 接口负责接收前端请求，调用应用服务层的阈值告警服务来完成实际的业务逻辑。

### TODO

1. 是否要加一个延时操作, 因为采集是异步的, 采集任务结束时不一定能拿到最新数据, 所以需要一个延时操作等待区域主控上传
2. 统一一下区域主控的命名, 目前有AreaController和RegionalController, 不排除还有别的
3. 将数据类型转为float32, 节约空间, float64精度有些浪费, float32小数点后6-7位足够了

# 实现记录

1. 定义告警表和告警历史表
2. 重构部分枚举, 让models包不依赖其他项目中的包
3. 创建仓库层对象(不包含方法)
4. 实现告警发送任务
5. 实现告警通知发送计划/全量采集计划改名
6. 实现设备阈值检查任务
7. 实现忽略告警和取消忽略告警接口及功能
8. 实现列表查询活跃告警和历史告警
9. 系统初始化时健康计划调整(包括增加延时任务)
10. 实现区域阈值告警任务
11. 实现区域阈值告警和设备阈值告警的增删改查
12. 实现任务11应的八个web接口
13. 实现根据区域ID或设备ID清空对应阈值告警任务
14. 设备和区域主控删除时清除对应区域阈值告警或设备阈值告警任务
15. 将所有Regional更改为Area
16. float64全部改float32
17. uint/uint64全部改为uint32