3. 报警数据模型设计

报警管理系统的可靠性,很大程度上取决于数据模型是否把“谁在什么设备上、按什么规则、在什么时间、以什么状态触发了什么报警”表达清楚。如果模型只设计一张“报警记录表”,把设备名称、规则阈值、处理人全部塞进同一行,短期能跑,长期必然出现数据冗余、状态不一致和统计口径混乱。本节围绕四个核心实体——设备、报警规则、报警记录、处理状态——完成概念模型到物理表结构的落地。

一、先厘清实体边界与主键策略

设备(device)是报警产生的物理载体,例如“杀菌釜 ST-01”“冷库压缩机 CP-03”。设备表保存设备编号、名称、类型、所属车间、安装位置和运行状态。主键建议使用自增整型 id,同时为业务编号 device_code 建唯一索引,避免业务人员改编号时牵连历史报警。

报警规则(alarm_rule)描述“什么条件算报警”。一条规则绑定一台设备(也可扩展为绑定设备类型),包含监测参数(如温度、压力、液位)、比较方式(大于、小于、区间外)、阈值、持续时间、报警等级和是否启用。规则与设备是多对一关系:一台设备可以配置多条规则,例如杀菌釜同时配置“温度超上限”和“压力超上限”。

报警记录(alarm_record)是规则被触发后产生的事实数据,属于典型的“事件型”实体,只追加、不随意修改。它记录触发时间、触发值、规则快照(阈值、等级)、关联设备和规则、当前处理状态。这里有一个关键设计决策:报警记录中应冗余保存触发时的阈值和等级,而不是每次查询都回查规则表。因为规则阈值后续可能被调整,历史报警必须保留“当时按什么标准判定的”,否则追溯会失真。

处理状态有两种建模方式:一是直接在报警记录表放一个 status 字段(如 0 未处理、1 处理中、2 已关闭、3 误报);二是独立建 alarm_handle 处理流水表,记录每次确认、派工、关闭的操作人、时间和备注。工程上推荐“状态字段 + 处理流水表”组合:状态字段用于快速筛选和看板统计,流水表用于审计追溯。若系统规模较小,也可只保留状态字段并附加 handler_id、handled_at、handle_remark 三个字段,但会丢失多次处理的过程记录。

设备 device PK id UK device_code device_name / type workshop / location run_status 报警规则 alarm_rule PK id FK device_id param_name(温度/压力…) compare_op / threshold duration_s / alarm_level is_enabled UK (device_id, param_name) 报警记录 alarm_record PK id FK device_id / rule_id trigger_time / trigger_value snapshot_threshold / level status(0未处理…3误报) handler_id / handled_at recover_time IDX (device_id, trigger_time) 处理流水 alarm_handle PK id FK alarm_id action(确认/派工/关闭) operator_id / op_time remark 1 : N 1 : N 触发 1 : N 产生 1 : N 实线:外键强关联 虚线:报警记录同时引用设备与规则

二、关系与约束:把业务规则写进数据库

从ER图可以看到三条主要关系:设备与规则是一对多,规则与报警记录是一对多,报警记录与处理流水是一对多。报警记录同时引用设备和规则,其中 device_id 用于按设备维度统计,rule_id 用于追溯判定依据。为避免规则被删除后历史报警失去来源,规则表建议采用逻辑删除(is_deleted),外键使用 ON DELETE RESTRICT。

几个容易被忽略的约束值得提前定义:报警记录的 trigger_time 与 device_id 建联合索引,支撑“某设备最近24小时报警”这类高频查询;status 用 TINYINT 并在应用层枚举,避免中文字符串直接入库;trigger_value 与 snapshot_threshold 使用 DECIMAL(10,2),不要用 FLOAT,防止阈值比较出现浮点误差。此外可加唯一约束 UK(rule_id, trigger_time) 防止采集端重复上报造成同一条报警重复入库。

三、物理表结构示例

以下给出核心表的建表语句(MySQL 语法,示例参数可按实际调整):

CREATE TABLE device (
  id            BIGINT PRIMARY KEY AUTO_INCREMENT,
  device_code   VARCHAR(32)  NOT NULL,
  device_name   VARCHAR(64)  NOT NULL,
  device_type   VARCHAR(32)  NOT NULL,
  workshop      VARCHAR(64),
  location      VARCHAR(128),
  run_status    TINYINT DEFAULT 1,      -- 1运行 0停机
  UNIQUE KEY uk_code (device_code)
);

CREATE TABLE alarm_rule (
  id            BIGINT PRIMARY KEY AUTO_INCREMENT,
  device_id     BIGINT NOT NULL,
  param_name    VARCHAR(32) NOT NULL,   -- temperature / pressure
  compare_op    VARCHAR(8)  NOT NULL,   -- GT / LT / OUT
  threshold     DECIMAL(10,2) NOT NULL,
  duration_s    INT DEFAULT 0,          -- 持续超限秒数
  alarm_level   TINYINT NOT NULL,       -- 1提示 2警告 3严重
  is_enabled    TINYINT DEFAULT 1,
  is_deleted    TINYINT DEFAULT 0,
  UNIQUE KEY uk_dev_param (device_id, param_name),
  CONSTRAINT fk_rule_dev FOREIGN KEY (device_id)
    REFERENCES device(id) ON DELETE RESTRICT
);

CREATE TABLE alarm_record (
  id                 BIGINT PRIMARY KEY AUTO_INCREMENT,
  device_id          BIGINT NOT NULL,
  rule_id            BIGINT NOT NULL,
  trigger_time       DATETIME NOT NULL,
  trigger_value      DECIMAL(10,2) NOT NULL,
  snapshot_threshold DECIMAL(10,2) NOT NULL,
  alarm_level        TINYINT NOT NULL,
  status             TINYINT DEFAULT 0, -- 0未处理 1处理中 2已关闭 3误报
  handler_id         BIGINT,
  handled_at         DATETIME,
  recover_time       DATETIME,
  KEY idx_dev_time (device_id, trigger_time),
  UNIQUE KEY uk_rule_time (rule_id, trigger_time)
);

四、状态流转与设计校验

处理状态不是随意赋值的字段,而应遵循明确流转:新报警写入时 status=0;操作员确认后置为 1 并写入 handler_id、handled_at;故障排除或误报判定后置为 2 或 3,同时写入 recover_time。每次状态变更都向 alarm_handle 追加一条流水,保证“谁在何时做了什么”可审计。需要强调的是,状态只能单向推进,已关闭的报警不允许回退为未处理,否则统计报表会出现重复计数。

设计完成后可用三个问题自检:第一,任意一条历史报警能否还原出触发时的设备、规则阈值和等级?第二,按设备、按等级、按时间段统计报警数量时,是否需要跨表关联大表?第三,规则阈值调整后,旧报警的判定依据是否仍然准确?若三个答案都令人满意,说明模型基本可用。实际开发中还应预留 ext_json 之类的扩展字段,用于存放不同设备类型的个性化信息,避免频繁改表。