1. 页面概览
1.1 是什么
「告警自愈」页(对应前端源码 action/web/src/views/apollo/AlertsPage.vue,页面标题「Apollo 告警与自愈」,对齐 TAD-09 告警与自愈)是 LightApollo 的告警规则、事件单、自愈、通知与静默的统一操作台。页面顶部是五张 summary 计数卡(告警规则数 / 触发事件数 / 未处理事件单 / 自愈执行数 / 自愈成功数),主体按 五个 Tab 把「检测 → 归并 → 处置 → 自愈 → 通知」闭环切开:Tab1「告警规则」配置触发条件(绑定健康检查或事件)、级别、防抖/冷却与通知渠道;Tab2「事件单」展示并处置归并后的事件;Tab3「自愈策略」维护自动修复动作(需审批的策略进入人工审批流)并展示自愈历史;Tab4「通知渠道」维护 webhook/email/钉钉/企微外发目标;Tab5「静默」在指定时间窗内屏蔽匹配的告警。
理解本页的关键是抓住后端 alert 包的两条异步链路,页面只是它们的控制面:一条是「评估」——后端 Scheduler 每 15s 驱动一次 EvaluateAll,对每条启用且绑定健康检查的规则读取 health_check_results 最新结果做条件匹配,命中则创建 firing 告警事件并按规则归并事件单(同规则持续触发不重复开单,只累积事件 id 并抬升级别);另一条是「恢复」——条件不再满足时把该规则 firing 事件置 resolved 并自动关闭未解决事件单(resolution_note 写 健康检查恢复,告警自动关闭)。页面不轮询任何运行态,看到的规则/事件单/策略/渠道/静默都是库表全量(写操作成功后才会重拉对应列表)。事件单打开的同时会联动自愈:命中绑定规则且 enabled 的自愈策略即生成自愈历史,自动型直接执行、需审批型挂 pending_approval 等人审批。
输入是用户对五类运维对象的增删改(规则条件 JSON、事件单处置 note、自愈动作、渠道 url、静默时间窗与 matcher),输出是各 Tab 的表格行、summary 计数与一行式操作结果提示。与相邻页的关系:「监控可观测」页负责健康检查(本页规则弹窗的下拉与评估依据都来自 health_checks/health_check_results),本页把「健康检查异常」翻译成「可处置的事件单 + 可执行的修复动作」,是 Apollo 健康闭环的出口。
典型使用链路:① 先到「监控可观测」页确认目标健康检查存在且启用(本页下拉才能选到);② 本页 Tab1「新建告警规则」:选触发类型 health_check、绑定健康检查、指标来源 response_time_ms、条件方式阈值比较(op >、value 2000)、级别 warning,可填防抖/冷却与通知渠道;③ 到 Tab3「新建自愈策略」绑定该规则、选 restart、勾选「需要审批后执行」;④ 等健康检查指标超阈值 → 后端评估触发 → Tab 事件单归并出现 open 事件单 → 自愈历史出现 pending_approval 行 → 本页「审批」后自动执行 restart;⑤ 健康恢复 → 事件单自动关闭;⑥ 若目标服务要发版,到 Tab5「新建静默」设时间窗让告警安静。已修复(提交 412e9c48):此前「各 Tab 首次切换不加载」「summary 两张卡恒 0」「pendingHistory 未定义致渲染异常」三处前端缺陷均已修复(各 Tab 在挂载时并行拉取、summary 补全自愈计数、待审批计数改为 pendingHistoryCount 计算属性),演示已无需绕行。
1.2 核心价值/能力表
| 能力 | 说明 | 对应页面操作 |
|---|---|---|
| 告警规则 | 绑定健康检查或事件,condition JSON 支持健康状态匹配与阈值比较,级别/防抖/冷却/通知渠道可配 | Tab1 规则列表 + 新建/编辑弹窗 + 启停开关 + 删除 |
| 事件单归并 | 同规则 firing 事件归并到同一 open 事件单并抬升级别;恢复自动关单 | Tab2 事件单列表 + 确认/解决弹窗 |
| 自愈策略 | restart/rollback/scale_up/scale_down/clean_disk 五种动作,可限制自动尝试次数 | Tab3 策略列表 + 新建/编辑 + 启停 + 删除 |
| 人工审批流 | 需审批策略先落 pending_approval,审批后才执行;拒绝则不再执行 | Tab3 自愈历史区「审批」/「拒绝」 |
| 通知渠道 | webhook 真实 HTTP POST(10s 超时),email/钉钉/企微为日志占位 | Tab4 渠道列表 + 新建 + 行内「测试」+ 删除 |
| 静默窗口 | 活动时间窗内匹配 matcher 的告警不再触发(空对象匹配全部) | Tab5 静默列表 + 新建弹窗 + 删除 |
| 顶部总览 | 规则数 / 触发事件数 / 未处理事件单 / 自愈执行数 / 自愈成功数五卡 | 页顶 summary-row 五张卡 |
1.3 一句话总结
告警自愈页把「规则 → 事件 → 归并事件单 → 自愈 → 通知 → 静默」全链路的控制面收进五个 Tab,让健康异常自动变成可处置、可审批、可通知、可屏蔽的运维动作。
2. 访问入口
2.1 路由与菜单
| 项 | 值 |
|---|---|
| 路由 path | /apollo/alerts |
| 路由 name | ApolloAlerts |
| meta.title | Apollo 告警自愈 |
| 侧边栏入口 | ApolloLayout 侧边栏「告警自愈」,位于「监控」之后、「安全合规」之前 |
| 前端源码 | action/web/src/views/apollo/AlertsPage.vue |
| API 客户端 | action/web/src/api/apolloClient.js |
| 路由注册 | action/web/src/router/index.js,/apollo 子路由 children 下第 463~468 行,component: ApolloAlertsPage,挂 ApolloLayout |
相邻页(侧边栏顺序):监控可观测 monitoring.html(前,健康检查即本页规则触发源)、安全合规 security.html(后)、部署与漂移 deployments.html(事件单处置/自愈动作常作用于部署目标)。
2.2 认证与权限
- 路由
requiresAuth: true:未登录访问被前端路由守卫拦截并跳/apollo/login(登录态为独立apollo_token,2026-09-12 修订后不再回退读取旧aip_token)。 - 全部端点位于 Apollo 后端 protected 组并按读写执行分层:列表/详情读
PermAlertRead(alert:read);规则/事件单/策略/渠道/静默的写操作(新建/编辑/删除、启停规则、事件单确认/解决)读PermAlertWrite;自愈执行向(策略启停、自愈记录审批/拒绝、渠道测试)读PermAlertExecute。403 由 apolloClient 统一alert('无权限执行该操作')。 - 规则弹窗的健康检查下拉跨模块读
GET /projects/:id/health-checks,要求PermMonitoringRead;失败被前端静默降级为空下拉(见 7.5)。 - 401(apollo_token 失效)由响应拦截器清 token 并跳
/apollo/login;本页无公开端点。
2.3 端口与 API 前缀
- Apollo 后端端口 18082。Vite 开发代理把
/apollo-api前缀 rewrite 为/api转发(后端路由注册在/api/v1),客户端 baseURL/apollo-api/v1。 - 统一响应 envelope
{code,message,data,request_id}:拦截器在code===0时把response.data解包为业务数据,页面const { data } = await apiClient.get(...)直接拿业务数组/对象;列表 data 为数组或{items},页面toList健壮解析。 - 项目 id 本页恒用常量
PROJECT_ID = 1(注释标明「后续可改为从 URL 参数 /project_id 读取」)。
3. 界面布局
+--------------------------------------------------------------+
| Apollo 告警与自愈 [新建告警规则] |
| [alert-info 顶部说明] [操作结果提示条(可关闭)] |
+--------------------------------------------------------------+
| [summary 五卡] 规则数|触发事件数|未处理事件单|自愈执行数|自愈成功数 |
+--------------------------------------------------------------+
| [Tab 条] 告警规则 | 事件单 | 自愈策略 | 通知渠道 | 静默 |
+--------------------------------------------------------------+
| 规则 Tab: [告警规则(N)卡片] |
| ID|名称|级别|触发类型|指标来源|条件|通知渠道|启用|操作 |
+--------------------------------------------------------------+
| 事件单 Tab: [事件单(N)卡片] |
| ID|标题|级别|状态|处置人|创建时间|操作(确认/解决) |
+--------------------------------------------------------------+
| 自愈策略 Tab: [自愈策略(N)卡 + 右下[新建自愈策略]] |
| ID|名称|动作类型|动作目标|触发规则|自动尝试|需审批|启用|操作 |
| [自愈历史卡] 标题行含「(待审批 N 条)」 |
| ID|事件单|动作类型|目标|状态|时间|操作(pending_approval 行) |
+--------------------------------------------------------------+
| 通知渠道 Tab: [通知渠道(N)卡 + 右下[新建通知渠道]] |
| ID|名称|类型|Webhook URL|启用|操作(测试/删除) |
+--------------------------------------------------------------+
| 静默 Tab: [静默(N)卡 + 右下[新建静默]] |
| ID|名称|开始时间|结束时间|匹配条件(matcher)|创建人|操作 |
+--------------------------------------------------------------+
| 弹窗:新建/编辑规则 | 事件单确认/解决 | 新建/编辑策略 | |
| 新建渠道 | 新建静默 |
+--------------------------------------------------------------+
各板块职责:
| 板块 | 职责 |
|---|---|
| 页头 + 顶部说明 + 提示条 | 标题「Apollo 告警与自愈」、「新建告警规则」入口、TAD-09 说明、全局操作结果提示(info/success/error 三型,可关闭) |
| summary 五卡 | GET /projects/1/alert-summary 五类计数(规则数/事件数/未处理事件单/自愈执行数/自愈成功数),失败保持全零不阻塞主列表 |
| Tab 条 | 五键切换 activeTab,纯本地状态;各 Tab 数据在页面挂载时并行拉取,切换即见数据(不再依赖写操作触发) |
| 告警规则卡 | 规则全量表格 + 行内启停/编辑/删除 |
| 事件单卡 | 归并事件单表格 + 行内确认/解决 |
| 自愈策略卡 + 自愈历史卡 | 策略 CRUD 表格 + 底部新建按钮;下方独立「自愈历史」卡收纳待审批记录与执行结果 |
| 通知渠道卡 | 渠道表格 + 行内测试/删除 + 底部新建 |
| 静默卡 | 静默表格 + 行内删除 + 底部新建 |
| 五个弹窗 | 新建/编辑规则、事件单处置、新建/编辑策略、新建渠道、新建静默 |
4. 交互元素
4.1 页头、顶部说明、summary 卡与 Tab 条
| 控件 | 位置 | 含义 | 必填/默认/可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 页面标题 | 页头 h2 | 页面身份标识 | 常驻 | Apollo 告警与自愈 | 无 | 与源码 h2 逐字一致 |
| 「新建告警规则」 | 页头右上(btn-primary) | 打开规则新建弹窗 | 始终可用 | openCreateRule() 重置表单并弹窗 | 无(提交才触发) | 默认 trigger_type=health_check、health_check_id=下拉第一个(无则空) |
| 顶部说明 | 标题下 alert-info | TAD-09 定位说明 | 常驻 | 纯文案 | 无 | 告警规则绑定健康检查(或事件),触发后归并生成事件单;自愈策略按规则自动执行(restart / rollback / scale_up / scale_down / clean_disk),需要审批的策略经人工审批后执行(TAD-09)。 |
| 操作结果提示条 | 说明下方 | 最近一次操作结果 | 有 alert.message 才显示 | 按 alert.type 着色;「关闭」按钮清空 | 无 | 单槽覆盖,无历史 |
| summary 规则数/触发事件数 | 五卡前两张 | 项目内规则总数、触发事件总数 | 挂载即拉 | 展示 summary.rules/summary.events | GET /projects/1/alert-summary | 后端键 rule_count/event_count 能命中前端别名;规则增删后随 fetchSummary 刷新 |
| summary 未处理事件单/自愈执行数/自愈成功数 | 五卡后三张 | 未关闭事件单数、自愈执行次数、自愈成功次数 | 同上 | 展示 summary.open_incidents/healingActionCount/healingSuccessCount | 同上 | 已修复(提交 412e9c48):前端别名已覆盖后端键 open_incident_count/healing_total/healing_success(AlertsPage.vue:904-906,后端 alert_handlers.go:1183-1188);后两张为 computed,接口缺键时回退用已加载的 history 统计(AlertsPage.vue:663-680) |
| Tab 条五键 | summary 下方 | 切换视图 | 常驻 | activeTab = t.key 纯本地切换 | 无 | 已修复(提交 412e9c48):onMounted 并行拉取 incidents/policies/history/channels/silences(AlertsPage.vue:1397-1407),切 Tab 即见数据,不再需要写操作触发首次加载 |
页面用一组 ref 维护状态:PROJECT_ID(常量 1)、rules/incidents/policies/history/channels/silences(六类列表)、healthChecks(规则弹窗下拉)、loading(各列表加载位,初始仅 rules:true)、busy(当前进行中的操作键,非空时全局按钮禁用)、alert(提示条单槽)、summary(五卡计数)、五个弹窗状态(ruleModal/incidentModal/policyModal/channelModal/silenceModal)。
页面 onMounted 并行拉取 fetchSummary / fetchRules / fetchIncidents / fetchPolicies / fetchHistory / fetchChannels / fetchSilences / fetchHealthChecks(AlertsPage.vue:1397-1407)。无定时轮询、无手动刷新按钮;各 Tab 数据均在挂载时加载一次,切换即见(详见 4.8)。
4.2 告警规则 Tab(行级控件)
卡片标题固定 告警规则({{ rules.length }})。空态 暂无告警规则,点击"新建告警规则"添加(项目 #{{ PROJECT_ID }})。;有数据渲染表格,列头依次 ID / 名称 / 级别 / 触发类型 / 指标来源 / 条件 / 通知渠道 / 启用 / 操作。各列取值:
| 列 | 取值规则(逐字对应源码) |
|---|---|
| ID | r.id 等宽显示 |
| 名称 | 加粗 r.name |
| 级别 | status-badge 按 severityClass 映射:critical 红(status-revoked)/ warning 橙(status-degraded)/ info 蓝(status-registering),文案取 severityLabel 如 critical(严重) |
| 触发类型 | triggerTypeLabel:health_check(健康检查) / event(事件) |
| 指标来源 | r.metric_source || '-'(health_status / response_time_ms / status_code) |
| 条件 | conditionSummary(r.condition):{health:{status}} 显 健康状态 = unhealthy;{threshold:{op,value}} 显 > 2000;扁平 status 显 健康状态 = x |
| 通知渠道 | channelSummary:数组 join ', ',空显 - |
| 启用 | 「已启用」(btn-success)/「已停用」(btn-outline)切换按钮,busy 非空禁用 |
4.2.1 行内启停开关按钮
| 控件 | 位置 | 含义 | 可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 「已启用/已停用」 | 启用列(btn-success/btn-outline) | 切换规则启用态 | busy 为空 | handleToggleRule:op = r.enabled ? 'disable' : 'enable',成功取 data?.enabled ?? op==='enable',提示 告警规则 #{id} 已启用/已停用(success)并 fetchRules() | POST /alert-rules/:id/enable 或 /disable | 失败提示 启用/停用告警规则 #{id} 失败:{msg}。未刷新 summary |
4.2.2 行内「编辑」「删除」
| 控件 | 位置 | 含义 | 可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 「编辑」 | 操作列(btn-outline) | 打开编辑弹窗回填 | busy 为空 | openEditRule 经 parseCondition 把 condition 还原为 mode/status/op/value 回填,通知渠道数组 join 逗号 | 无(提交才触发) | 编辑弹窗标题 编辑告警规则 #{id};!=/= 等后端支持的 op 不在下拉选项内时选择框显示空但值保留(见 8) |
| 「删除」 | 操作列(btn-danger) | 删除规则 | busy 为空 | confirm('确定删除告警规则 "{name}"(#id)吗?') 通过后 DELETE,提示 告警规则已删除(success)并 fetchRules()+fetchSummary() | DELETE /alert-rules/:id | 确认文案逐字见左;删除后绑定该规则的自愈策略 trigger_rule_id 悬空不再触发;失败提示 删除告警规则失败:{msg} |
4.3 告警规则新建/编辑弹窗
| 控件 | 位置 | 含义 | 必填/默认/可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 弹窗标题 | 弹窗头 | 标识当前模式 | create 显 新建告警规则,edit 显 编辑告警规则 #{id} | - | 无 | 点遮罩 @click.self 或「关闭」可关 |
| 名称(name)* | 表单首行 | 规则名称 | 必填;placeholder 如 web 响应超时告警 | 空名前端拦截提示 告警规则名称(name)必填 | - | 保存 payload name trim |
| 级别(severity)* | 级别下拉 | 告警级别 | 必填;三选 critical(严重)/warning(警告)/info(提示) | 写入 severity | - | 默认 warning |
| 触发类型(trigger_type)* | 触发类型下拉 | 触发来源 | 必填;health_check(健康检查)/event(事件) | 切换 health_check 时显示条件/指标区 | - | 默认 health_check;event 类型不展示条件表单(后端对 event 触发的评估见 8 边界) |
| 健康检查(health_check_id) | 健康检查下拉 | 绑定探针 | 下拉含 不绑定(事件触发) + 各 {name}(#{id}) | 绑定后指标/条件区可用 | 下拉数据来自 GET /projects/1/health-checks(挂载时拉) | health_check_id 默认取下拉第一项(healthChecks[0]?.id,空则 '');选了 health_check 类型但留空绑定时规则不会参与评估(见 7.5) |
| 指标来源(metric_source) | 下拉(health_check 时显) | 阈值比较的指标 | 三选 health_status(健康状态)/response_time_ms(响应时长)/status_code(状态码) | 写入 metric_source | - | 默认 health_status |
| 条件方式 | 下拉(health_check 时显) | 条件结构 | 健康状态匹配/阈值比较(op+value) | 切换 health 与 threshold 子表单 | - | 默认 health |
| 匹配健康状态(condition.status) | 下拉 | 健康匹配目标 | healthy(健康)/degraded(降级)/unhealthy(不健康) | 生成 condition:{health:{status}} | - | 默认 unhealthy |
| 比较符(condition.op) | 下拉 | 阈值比较符 | >/>=/</<=/== 五选(文案 >(大于) 等) | 生成 condition:{threshold:{op,value}} | - | 后端支持 = 与 !=,UI 未提供(见 8) |
| 阈值(condition.value) | number step=any | 阈值数值 | placeholder 如 2000 | Number(condition_value) 入 payload | - | 空值 Number(undefined)=NaN,阈值条件恒 false(见 7.6) |
| 防抖窗口(duration_seconds) | number min=0 | 防抖秒数 | placeholder 秒 | Number()||0 入 payload | - | 与 cooldown 取 max 作为抑制窗口(见 5.4) |
| 冷却时间(cooldown_seconds) | number min=0 | 冷却秒数 | placeholder 秒 | Number()||0 入 payload | - | 同上 |
| 通知渠道(notification_channels,渠道名,逗号分隔) | input | 外发渠道名列表 | placeholder 如 ops-webhook,dingtalk | 逗号 split + trim + filter 后数组入 payload(空则不携带) | - | 下方 muted 提示 可用渠道:{渠道名顿号}或暂无渠道(Tab 通知渠道中创建) |
| 「取消」 | 弹窗底部 | 关闭 | - | ruleModal.visible=false | 无 | 不校验 |
| 「创建/保存」 | 弹窗底部(submit) | 提交 | busy 为空;进行中按钮显示 保存中... | create POST 成功提示 告警规则创建成功(id={id}),edit PUT 成功提示 告警规则 #{id} 更新成功;成功后关弹窗并 fetchRules()+fetchSummary() | POST /projects/1/alert-rules / PUT /alert-rules/:id | 失败提示 保存告警规则失败:{msg} |
payload 组装(buildRulePayload):固定 {name, trigger_type, severity, duration_seconds, cooldown_seconds, condition};trigger_type==='health_check' 时带 metric_source 且 health_check_id 非空时带 Number(health_check_id),event 类型 metric_source 置空串;condition 由 buildRuleCondition 按条件方式生成 {health:{status}} 或 {threshold:{op,value}},event 类型返回 {}。编辑保存为全量 PUT,会把未展示字段(如 event 类型的旧 metric_source)按当前表单覆盖。
4.4 事件单 Tab(含确认/解决弹窗)
卡片标题固定 事件单({{ incidents.length }})。空态 暂无事件单,一切正常。;表格列头 ID / 标题 / 级别 / 状态 / 处置人 / 创建时间 / 操作。取值:标题加粗 inc.title;级别徽标同规则页;状态徽标取 inc.status || 'open',incidentStatusClass 映射 open 红 / acknowledged 橙 / investigating 蓝 / resolved 绿;处置人 inc.acknowledged_by || '-';时间 formatTime(inc.created_at)。
| 控件 | 位置 | 含义 | 可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 「确认」 | 操作列(btn-outline) | 确认事件单(认领) | inc.status === 'open' || inc.status === 'investigating' 才渲染;busy 为空 | openIncidentAction(inc,'acknowledge') 弹窗 | 提交才触发 | 已解决/已确认的行不显示确认按钮 |
| 「解决」 | 操作列(btn-success) | 关闭事件单 | inc.status !== 'resolved' 才渲染;busy 为空 | openIncidentAction(inc,'resolve') 弹窗 | 提交才触发 | 已解决行不显示 |
| 处置弹窗标题 | 弹窗头 | 标识动作 | - | 确认事件单 #{id} / 解决事件单 #{id} | 无 | - |
| 处置说明(note) | textarea rows=3 | 处置备注 | placeholder 按动作变:acknowledge 如:已通知值班同学跟进排查;resolve 如:服务已重启,指标恢复正常 | 提交 body {note: note.trim()} | POST /incidents/:id/acknowledge 或 /resolve | 注意:acknowledge 后端只记当前用户名为处置人、丢弃 note(见 5.4/8);resolve 才会把 note 写入 resolution_note |
| 「取消」/「确认或解决」 | 弹窗底部 | 关闭/提交 | 提交时 busy 为空;进行中按钮显示 提交中... | 成功提示 事件单 #{id} 已确认/已解决(状态:{status})(success),关弹窗并 fetchIncidents()+fetchSummary() | 同上 | 失败提示 处理事件单 #{id} 失败:{msg}。已修复(提交 412e9c48):事件单列表已在 onMounted 拉取(AlertsPage.vue:1401),进入 Tab 即见数据,不再出现「无数据行即无入口触发首次加载」的结构性空态 |
4.5 自愈策略 Tab(策略 + 自愈历史)
策略卡片标题固定 自愈策略({{ policies.length }}),空态 暂无自愈策略,点击"新建自愈策略"添加。;表格列头 ID / 名称 / 动作类型 / 动作目标 / 触发规则 / 自动尝试 / 需审批 / 启用 / 操作。取值:动作类型徽标按 actionClass(restart 蓝 / rollback 橙 / scale_up、scale_down 绿 / clean_disk 灰),文案 actionLabel 如 restart(重启);动作目标 p.action_target || '-';触发规则 ruleName(p.trigger_rule_id)(命中当前 rules 显示 名称(#id),未命中显 #id);自动尝试 p.max_auto_attempts;需审批徽标 需审批(status-degraded)/自动(status-offline);启用列按钮同规则页(「已启用/已停用」)。卡片右下角「新建自愈策略」。
策略行内控件:
| 控件 | 位置 | 含义 | 可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 启停按钮 | 启用列 | 切换策略启用态 | busy 为空 | handleTogglePolicy:op = p.enabled ? 'disable' : 'enable',成功提示 自愈策略 #{id} 已启用/已停用 并 fetchPolicies() | POST /healing-policies/:id/enable 或 /disable | 该端点需 PermAlertExecute;失败提示 启用/停用自愈策略 #{id} 失败:{msg} |
| 「编辑」 | 操作列 | 编辑回填 | busy 为空 | openEditPolicy 回填全部字段 | 无(提交才触发) | 弹窗标题 编辑自愈策略 #{id} |
| 「删除」 | 操作列(btn-danger) | 删除策略 | busy 为空 | confirm('确定删除自愈策略 "{name}"(#id)吗?') 通过后 DELETE,提示 自愈策略已删除 并 fetchPolicies() | DELETE /healing-policies/:id | 已产生的自愈历史记录保留;失败提示 删除自愈策略失败:{msg} |
| 「新建自愈策略」 | 策略卡右下(btn-primary) | 打开新建 | 始终可用 | openCreatePolicy 重置表单 | 无 | 默认 trigger_rule_id=rules[0]?.id(无规则则 null)、action_type=restart、max=1 |
4.5.1 自愈策略新建/编辑弹窗
| 字段 | 位置 | 含义 | 必填/默认 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 名称(name)* | 首行 | 策略名 | 必填;placeholder 如 web 自动重启策略 | trim 入 payload | - | - |
| 动作类型(action_type)* | 下拉 | 修复动作 | 必填;五选 restart(重启)/rollback(回滚)/scale_up(扩容)/scale_down(缩容)/clean_disk(清理磁盘) | 写入 action_type | - | 默认 restart |
| 触发规则(trigger_rule_id)* | 下拉 | 绑定告警规则 | 必填;选项为当前 rules 各 {name}(#{id}) | Number(trigger_rule_id) 入 payload | - | 无规则时下拉为空、表单无法提交(校验见下) |
| 动作目标(action_target) | input | 动作目标 | 可空;placeholder 如 env-1:web / svc 名 | trim 或 '' 入 payload | - | 透传给 ActionExecutor |
| 最大自动尝试次数(max_auto_attempts) | number min=0 | 成功次数上限 | 默认 '1' | Number()||0 入 payload | - | 0=不限制(见 5.4 canAttempt) |
| 需人工审批 | checkbox | 是否需审批后执行 | 默认不勾选 | requires_approval bool 入 payload | - | 勾选文案 需要审批后执行 |
提交校验(handleSavePolicy):名称空或 trigger_rule_id 空 → 提示 自愈策略名称与触发规则(trigger_rule_id)必填。create POST /projects/1/healing-policies 成功提示 自愈策略创建成功(id={id});edit PUT /healing-policies/:id 成功提示 自愈策略 #{id} 更新成功;成功后关弹窗并 fetchPolicies()(不刷新历史);失败提示 保存自愈策略失败:{msg}。
4.5.2 自愈历史卡与审批/拒绝
自愈历史卡片标题行:自愈历史 + muted (待审批 {{ pendingHistory.length }} 条)。空态 暂无自愈历史。;表格列头 ID / 事件单 / 动作类型 / 目标 / 状态 / 时间 / 操作。状态徽标 healingStatusClass:pending_approval 橙 / approved、executing 蓝 / success 绿 / failed 红 / denied 灰。
| 控件 | 位置 | 含义 | 可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 「审批」 | 历史行(btn-success) | 通过待审批自愈 | h.status === 'pending_approval' 才渲染;busy 为空 | handleApproveHistory(h,'approve') POST,成功提示 自愈记录 #{id} 已审批(状态:{status})(success)并 fetchHistory() | POST /healing-history/:id/approve | 后端审批通过后立即执行(见 5.4);失败提示 审批自愈记录 #{id} 失败:{msg} |
| 「拒绝」 | 历史行(btn-danger) | 拒绝待审批自愈 | 同上 | 先 confirm('确定拒绝自愈记录 #{id} 吗?将不再执行该动作。') 通过后 POST,成功提示 自愈记录 #{id} 已拒绝(状态:{status}) 并 fetchHistory() | POST /healing-history/:id/deny | 拒绝即终态(denied),不再执行;失败提示 拒绝自愈记录 #{id} 失败:{msg} |
已修复(提交 412e9c48):标题行待审批计数改用 pendingHistoryCount 计算属性(AlertsPage.vue:663 按 history 过滤 pending_approval,模板 :248 引用),不再引用未声明的 pendingHistory,进入本 Tab 不再抛渲染异常。历史卡数据已在 onMounted 拉取(AlertsPage.vue:1403),进入 Tab 即显示存量历史。
4.6 通知渠道 Tab
卡片标题固定 通知渠道({{ channels.length }}),空态 暂无通知渠道,点击"新建通知渠道"添加。;表格列头 ID / 名称 / 类型 / Webhook URL / 启用 / 操作。类型徽标 channelClass:webhook 绿 / email 蓝 / dingtalk 橙 / wecom 灰,文案 channelLabel:webhook/email/dingtalk(钉钉)/wecom(企微);URL 列 channelUrl(ch.config)(字符串 config 先 JSON.parse 再取 url,空显 -,mono url-cell 超长省略);启用列纯展示徽标 启用(status-online)/停用(status-offline)。卡片右下「新建通知渠道」。
| 控件 | 位置 | 含义 | 可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 「测试」 | 操作列(btn-outline) | 真实试发渠道 | busy 为空 | handleTestChannel POST,ok = data.success===true||data.ok===true||data.sent===true,msg = data?.message || (ok ? '测试消息已发送' : '测试发送失败'),提示 通知渠道 "{name}" 测试:{msg}(按 ok 着 success/error) | POST /notification-channels/:id/test | webhook 真实 POST 10s 超时;email/钉钉/企微返回占位 note 恒 success;失败(catch)提示 通知渠道 "{name}" 测试失败:{errMsg} |
| 「删除」 | 操作列(btn-danger) | 删除渠道 | busy 为空 | confirm('确定删除通知渠道 "{name}"(#id)吗?') 通过后 DELETE,提示 通知渠道已删除 并 fetchChannels() | DELETE /notification-channels/:id | 规则里若仍引用该渠道名,发送时查不到同名渠道即跳过(见 8);失败提示 删除通知渠道失败:{msg} |
| 「新建通知渠道」 | 卡片右下(btn-primary) | 打开新建弹窗 | 始终可用 | 重置表单 | 无 | 弹窗标题 新建通知渠道(项目 #{{ PROJECT_ID }}) |
新建渠道弹窗字段:名称(name)* placeholder 如 ops-webhook;类型(type)* 下拉四选(默认 webhook);Webhook URL(config.url)placeholder 如 https://hooks.example.com/xxx,下方 muted 提示 webhook 类型真实发送(HTTP POST,10s 超时);邮件/钉钉/企微仅模拟。 校验:名称空 → 通知渠道名称(name)必填。payload {name:trim, type, config:{url:trim}}(非 webhook 类型也一律包 config.url)。POST /projects/1/notification-channels 成功提示 通知渠道创建成功(id={id}),失败 保存通知渠道失败:{msg}。
4.7 静默 Tab
卡片标题固定 静默({{ silences.length }}),空态 暂无静默,点击"新建静默"添加。;表格列头 ID / 名称 / 开始时间 / 结束时间 / 匹配条件(matcher)/ 创建人 / 操作。matcher 列 stringify JSON 化(空显 -);时间列 formatTime;创建人 s.created_by || '-';操作列仅「删除」。卡片右下「新建静默」。
| 字段 | 位置 | 含义 | 必填/默认 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 名称(name)* | 弹窗首行 | 静默名 | 必填;placeholder 如 发布窗口静默 | trim 入 payload | - | - |
| 开始/结束时间(starts_at/ends_at)* | 两个 datetime-local | 静默窗口 | 必填 | new Date(x).toISOString() 入 payload | - | 前端先校验结束须晚于开始 |
| 匹配条件(matcher JSON) | textarea rows=3 | 匹配器 | 可空(空对象匹配全部) | JSON.parse 后入 payload | - | placeholder {"severity":"critical"} 或 {"rule_id":3},空对象匹配全部 |
前端校验(handleSaveSilence,拦截文案逐字):三必填缺失 → 静默名称与时间窗(starts_at/ends_at)必填;ends_at <= starts_at → 结束时间必须晚于开始时间;matcher 非空且 JSON 非法 → matcher 不是合法 JSON:{e.message}。POST /projects/1/silences 成功提示 静默创建成功(id={id}),失败 保存静默失败:{msg};删除经 confirm('确定删除静默 "{name}"(#id)吗?'),成功 静默已删除,失败 删除静默失败:{msg},均刷新 fetchSilences()。后端另有同名校验 静默结束时间须晚于开始时间 双保险(见 5.4)。
4.8 全局互斥与数据刷新面
全页唯一「写互斥位」是字符串 busy:写操作开始置为操作键(saveRule/savePolicy/saveChannel/saveSilence/incidentAction/enable-{id}/disable-{id}/delRule-{id}/delPol-{id}/testCh-{id}/approve-{id}/deny-{id} 等),结束时在 finally 清空;busy 非空时全页按钮与开关 :disabled="!!busy" 防并发写。
各写操作成功后的刷新面(逐字对应源码):
- 规则 保存/删除:
fetchRules() + fetchSummary();规则启停:仅fetchRules(); - 事件单 确认/解决:
fetchIncidents() + fetchSummary(); - 自愈策略 保存/删除/启停:仅
fetchPolicies();自愈历史 审批/拒绝:仅fetchHistory(); - 通知渠道 保存/删除:仅
fetchChannels();渠道测试:不刷新; - 静默 保存/删除:仅
fetchSilences()。
首次加载已补齐(提交 412e9c48):onMounted 现并行拉取 incidents/policies/history/channels/silences(AlertsPage.vue:1397-1407),切换 Tab 即见存量数据,无需先执行一次写操作。无定时轮询:挂载后仅各写操作刷新对应列表,后端异步触发的新事件单/新历史需刷新页面或重进才能看到;summary 也只在 规则保存/删除 与 事件单处置 后刷新。
5. 后端关联
5.1 API 客户端
action/web/src/api/apolloClient.js:axios 实例 baseURL /apollo-api/v1、timeout 30000ms、请求拦截器对所有请求附加 Authorization: Bearer <token>(token 仅取 apollo_token,不回退 aip_token)。响应拦截器:HTTP 2xx 且 code===0 时把 response.data 解包为业务数据;HTTP 2xx 但 code!==0 拒绝;4xx/5xx 提取 body.message || body.error 并把 message 别名写入 error.response.data.error 后 reject——401 清 token 跳 /apollo/login,403 alert('无权限执行该操作')。页面 catch 统一以 err.response?.data?.message || err.response?.data?.error || err.message || '未知错误' 展示后端错误。
5.2 端点表
以下路径均为相对 baseURL /apollo-api/v1(Vite rewrite 后后端收 /api/v1/...)。项目 id 页面恒用 1。权限点映射与注册行号见 server.go 910~942 行。
| 方法 | 路径 | 权限 | 页面触发点 / 说明 |
|---|---|---|---|
| GET | /projects/1/alert-summary | PermAlertRead | 页顶 summary 五卡 |
| POST | /projects/1/alert-rules | PermAlertWrite | 新建告警规则 |
| GET | /projects/1/alert-rules | PermAlertRead | 规则列表 |
| GET | /alert-rules/:id | PermAlertRead | (后端有,页面未调用) |
| PUT | /alert-rules/:id | PermAlertWrite | 编辑规则 |
| DELETE | /alert-rules/:id | PermAlertWrite | 删除规则 |
| POST | /alert-rules/:id/enable | PermAlertWrite | 启用规则 |
| POST | /alert-rules/:id/disable | PermAlertWrite | 停用规则 |
| GET | /projects/1/alert-events | PermAlertRead | (后端有,页面无事件列表 Tab) |
| GET | /projects/1/incidents | PermAlertRead | 事件单列表(挂载时并行拉取,见 4.8) |
| POST | /incidents/:id/acknowledge | PermAlertWrite | 确认事件单 |
| POST | /incidents/:id/investigate | PermAlertWrite | (后端有,页面无入口) |
| POST | /incidents/:id/resolve | PermAlertWrite | 解决事件单 |
| POST | /projects/1/healing-policies | PermAlertWrite | 新建自愈策略 |
| GET | /projects/1/healing-policies | PermAlertRead | 策略列表 |
| GET | /healing-policies/:id | PermAlertRead | (后端有,页面未调用) |
| PUT | /healing-policies/:id | PermAlertWrite | 编辑策略 |
| DELETE | /healing-policies/:id | PermAlertWrite | 删除策略 |
| POST | /healing-policies/:id/enable | PermAlertExecute | 启用策略 |
| POST | /healing-policies/:id/disable | PermAlertExecute | 停用策略 |
| POST | /healing-history/:id/approve | PermAlertExecute | 审批自愈记录 |
| POST | /healing-history/:id/deny | PermAlertExecute | 拒绝自愈记录 |
| GET | /projects/1/healing-history | PermAlertRead | 自愈历史(历史按 created_at 倒序返回) |
| POST | /projects/1/notification-channels | PermAlertWrite | 新建渠道 |
| GET | /projects/1/notification-channels | PermAlertRead | 渠道列表 |
| GET | /notification-channels/:id | PermAlertRead | (后端有,页面未调用) |
| PUT | /notification-channels/:id | PermAlertWrite | (后端有,页面无编辑入口) |
| DELETE | /notification-channels/:id | PermAlertWrite | 删除渠道 |
| POST | /notification-channels/:id/test | PermAlertExecute | 渠道测试 |
| POST | /projects/1/silences | PermAlertWrite | 新建静默 |
| GET | /projects/1/silences | PermAlertRead | 静默列表 |
| DELETE | /silences/:id | PermAlertWrite | 删除静默 |
| GET | /projects/1/health-checks | PermMonitoringRead | 规则弹窗健康检查下拉(跨模块,monitoring 侧端点) |
5.3 响应结构示例
统一 envelope 解包后(code===0 的 data)。alert-summary(GET /projects/1/alert-summary,真实后端形状):
{
"project_id": 1,
"rule_count": 3,
"event_count": 5,
"incident_count": 2,
"open_incident_count": 1,
"healing_total": 4,
"healing_success": 3
}
字段含义:rule_count 项目内规则总数;event_count 这些规则产生的告警事件总数(firing+resolved);incident_count 事件单总数;open_incident_count 未关闭(status != resolved)事件单数;healing_total 自愈已执行次数(executing/success/failed);healing_success 其中成功(success)次数(后端 alert_handlers.go:1181-1189)。前端映射已对齐(提交 412e9c48):页面 field 别名现覆盖 open_incident_count/healing_total/healing_success(AlertsPage.vue:904-906),五张 summary 卡均正常显示;后两张卡为 computed,接口缺键时回退用已加载 history 统计(AlertsPage.vue:663-680)。
告警规则单项(GET /projects/1/alert-rules data 数组元素,即 AlertRule):
{
"id": 1,
"project_id": 1,
"name": "web 响应超时告警",
"trigger_type": "health_check",
"metric_source": "response_time_ms",
"health_check_id": 3,
"condition": { "threshold": { "op": ">", "value": 2000 } },
"severity": "warning",
"duration_seconds": 30,
"cooldown_seconds": 60,
"notification_channels": ["ops-webhook"],
"enabled": true,
"created_at": "2026-09-07T02:00:00+08:00",
"updated_at": "2026-09-07T03:21:00+08:00"
}
字段说明(AlertRule):trigger_type health_check/event;metric_source health_status/response_time_ms/status_code(event 类型页面存空串);health_check_id 指针字段、未绑定时缺省;condition 为 {health:{status}} 或 {threshold:{op,value}} JSON(缺省则不出现);notification_channels 渠道名数组(缺省则不出现);enabled 用 *bool,nil 视为启用、显式 false 为停用。
事件单单项(GET /projects/1/incidents data 数组元素,即 Incident):
{
"id": 1,
"project_id": 1,
"title": "告警规则「web 响应超时告警」触发",
"severity": "warning",
"status": "open",
"source_alert_ids": { "rule_id": 1, "event_ids": [5, 8] },
"acknowledged_by": "",
"resolution_note": "",
"created_at": "2026-09-07T02:01:00+08:00"
}
字段说明:title 由后端拼 告警规则「{规则名}」触发;status open/acknowledged/investigating/resolved;source_alert_ids 为 IncidentSource(rule_id + 归并的 event_ids);acknowledged_by 确认人(未确认空串);resolution_note 解决备注(自动关闭为 健康检查恢复,告警自动关闭);resolved_at 指针字段、未解决时缺省。acknowledge 响应也是 Incident——确认后 status 变 acknowledged、acknowledged_by 写当前用户名。
自愈策略单项(GET /projects/1/healing-policies data 数组元素,即 SelfHealingPolicy):
{
"id": 1,
"project_id": 1,
"name": "web 自动重启策略",
"trigger_rule_id": 1,
"action_type": "restart",
"action_target": "env-1:web",
"max_auto_attempts": 3,
"requires_approval": false,
"enabled": true,
"created_at": "2026-09-07T02:00:00+08:00",
"updated_at": "2026-09-07T03:21:00+08:00"
}
自愈历史单项(GET /projects/1/healing-history data 数组元素,即 HealingHistory;result 缺省则不出现):
{
"id": 1,
"incident_id": 1,
"policy_id": 1,
"action_type": "restart",
"target": "env-1:web",
"status": "pending_approval",
"created_at": "2026-09-07T02:02:00+08:00"
}
状态取值 pending_approval/approved/executing/success/failed/denied。result 承载审计与执行详情:审批路径写入 {approved_by, approved_at}(拒绝写 {denied_by, denied_at}),执行完成合并执行器结果并保留审批字段(mergeResult 既有键保留、新键覆盖),例如:
{
"id": 1,
"incident_id": 1,
"policy_id": 1,
"action_type": "restart",
"target": "env-1:web",
"status": "success",
"result": {
"approved_by": "alice",
"approved_at": "2026-09-07T02:02:10+08:00",
"action": "restart",
"target": "env-1:web",
"task_id": 42
},
"created_at": "2026-09-07T02:02:00+08:00"
}
通知渠道单项(GET /projects/1/notification-channels data 数组元素):
{
"id": 1,
"project_id": 1,
"name": "ops-webhook",
"type": "webhook",
"config": { "url": "https://hooks.example.com/xxx" },
"enabled": true,
"created_at": "2026-09-07T02:00:00+08:00"
}
静默单项(GET /projects/1/silences data 数组元素):
{
"id": 1,
"project_id": 1,
"name": "发布窗口静默",
"starts_at": "2026-09-07T10:00:00+08:00",
"ends_at": "2026-09-07T22:00:00+08:00",
"matcher": { "severity": "critical" },
"created_by": "admin",
"created_at": "2026-09-07T09:00:00+08:00"
}
渠道测试:webhook 成功 {channel_id, type:"webhook", test:true, status_code:200};非 webhook 占位成功 {channel_id, type, test:true, note:"该渠道类型为占位实现,仅记录日志"};失败 envelope 消息有 解析 webhook 渠道配置失败: ... / webhook 渠道缺少 url 配置 / Webhook 测试请求失败: ... / Webhook 测试返回非 2xx 状态码: ...。
错误示例(envelope fail):删除/审批不存在的记录返回 xxx not found;对已解决事件单确认返回 400、incident {id} 已解决,不可确认;对非待审批记录审批返回 400、healing history {id} 状态非待审批(当前 {status})。
5.4 关键机制
评估引擎(EvaluateHealthCheck)。后端 alert 包 RuleEngine 以 Scheduler 周期驱动(scheduler.go:interval<=0 时默认 15s,server 以显式 interval 调用 Start)。每次对一条健康检查评估(EvaluateHealthCheck):① 直查 health_check_results 最新一条(无结果直接返回,不评估);② 取绑定该检查的 enabled=true 规则;③ 逐规则先做静默判定(IsSilenced(ruleID, severity),命中活动静默即 continue 跳过,日志 告警被静默,跳过);④ 条件匹配 matchesCondition——condition.health.status 精确等于结果状态才算命中;condition.threshold 对 metric_source(response_time_ms/status_code)按 op 用 compareValue 比较(支持 > >= < <= == = !=);两者都没有时兜底「非 healthy 即告警」;⑤ 不命中走恢复:ResolveFiringByRule 置该规则 firing 事件 resolved,有清理到(n>0)再 autoCloseIncident——把该规则未关闭事件单按 健康检查恢复,告警自动关闭 备注关单;⑥ 命中再经 canFire 防抖冷却:同规则最近事件仍 firing 不重复触发,否则以 max(duration,cooldown) 秒内不重复;⑦ 通过则写一条 firing 事件(Source 带健康检查结果快照)→ OpenIncident 归并 → Notify 通知 → MaybeHeal 自愈。EvaluateAll 遍历所有 enabled 且 trigger_type=health_check 规则的检查去重评估(event 类型规则不在本轮评估范围)。
事件单归并与生命周期。OpenIncident:按规则查现有未关闭事件单(FindOpenByRule)——存在则只把新事件 id 追加进 source_alert_ids.event_ids 并 maxSeverity 抬升级别(critical>warning>info),日志 事件单归并告警事件;不存在才新建,title 告警规则「%s」触发。状态机 open→acknowledged→investigating→resolved(TAD-09)。Acknowledge:已 resolved 拒绝(incident %d 已解决,不可确认),否则置 acknowledged 并写 AcknowledgedBy=当前用户名——后端不读请求 body,页面确认弹窗填的 note 不会落库。Resolve:幂等(已解决直接返回);同事务内把事件单置 resolved、写 ResolutionNote=note、ResolvedAt,并闭合 source_alert_ids 里仍 firing 的源事件(置 resolved 留 resolved_at),已解决事件单人工收口后不再与 firing 事件并存(O-048 语义)。Investigate:open/acknowledged 可转 investigating,已解决拒绝、已 investigating 幂等;页面没有 investigate 入口。
通知(Notifier)。规则触发事件后 Notify 按规则的 notification_channels 渠道名数组找启用的渠道:webhook 类型真实 http.Post(10s 超时客户端),payload 形如 {event_id, rule_id, severity, status, source, fired_at, channel, sent_at};email/dingtalk/wecom 仅日志占位(通知渠道(占位)已记录)。发送失败仅记日志,不影响事件单与自愈主链路。
自愈管线(SelfHealing)。MaybeHeal 在事件单打开后触发:取绑定该规则且 enabled 的策略;HasActive 查「同一事件单+策略」是否已有进行中记录(防重复);canAttempt 统计该策略 success 历史数须 < MaxAutoAttempts(0=不限制),超限跳过并 logger.Warn;新建 HealingHistory——RequiresApproval=false 直接 execute(初态 approved),true 初态 pending_approval 等审批。execute:置 executing → 调 server 注入的 apolloActionExecutor(restart/scale_up/scale_down 写 agent_task 由节点 Agent 落地、rollback 走 GitOps 回滚、clean_disk 为模拟清理)→ 成功置 success、失败置 failed,结果经 mergeResult 合并(保留既有审计键,避免审批字段被执行结果覆盖)。Approve/Deny:仅 pending_approval 可操作(否则 400 状态非待审批(当前 {status}));approve 先写 result.approved_by/approved_at 再立即执行;deny 写 denied_by/denied_at 置终态 denied、不再执行。
静默匹配(SilenceManager)。IsSilenced 取当前活动窗口(ListActive(now),starts_at<=now<ends_at)内全部静默逐条 matchSilence。matcher 仅识别两键:rule_id(与规则 id 数值比较,浮点/uint 均兼容)与 severity(字符串相等);空对象 {} 匹配全部;其它键静默忽略。创建校验:名称空(静默名称不能为空)、结束须晚于开始(静默结束时间须晚于开始时间);CreatedBy 为空兜底 system。
调度器节奏:alert Scheduler 默认 15s 驱动 EvaluateAll(评估/恢复/自愈联动都在评估 tick 的调用链上完成,审批执行是另一条即时路径);监控侧 monitoring.Scheduler 以 30s 对健康检查各探测一轮。因此健康检查结果至少落后探测一个周期,规则触发评估又落后结果一个周期,演示时留出约 15~45s 观察窗。
6. 权限与安全
- 认证:全部端点位于 Apollo protected 组(
/api/v1),JWT 无效返回 401,前端拦截器清 token 并跳/apollo/login;本页无公开端点、无匿名可调接口。 - 权限点三分:读 PermAlertRead(各列表/详情/summary);写 PermAlertWrite(规则/事件单/策略/渠道/静默的新建编辑删除与规则启停、事件单确认/解决);执行 PermAlertExecute(策略启停、自愈记录审批/拒绝、渠道测试)——把「会真实外发/会真执行动作」的操作单独划到 execute,便于授权收紧。403 统一
alert('无权限执行该操作')。 - 写操作防护:删除规则/策略/渠道/静默、拒绝自愈记录均经
window.confirm二次确认;approve 无 confirm(一个点击即真实执行动作,属演示产品的取舍,生产建议补二次确认)。 - 并发与幂等:全局
busy互斥串行化写操作;事件单解决幂等;自愈按 HasActive/成功次数双重防重复与限次;渠道测试是唯一会向外部发真实请求的操作(webhook POST),仅限 execute 权限。 - 作用域:事件单 investigate 等
/incidents/:id/*路径不在/projects/:id/下,后端对该类子资源操作自查项目成员归属(super 直通),防跨项目越权操作(本页虽无 investigate 入口,ack/resolve 相同路径形态受益于同套防护)。
7. 常见问题与排错
以下现象均由 AlertsPage.vue 的 catch 分支、后端 handler/service 代码与既有测试归纳,可溯源复现;此前确属前端缺陷者已在提交 412e9c48 修复(见各条与 8 章)。
页顶「未处理事件单」「自愈执行数」「自愈成功数」三张卡(历史现象,已修复):旧版前端
field别名未覆盖后端单数键open_incident_count,且当时后端无自愈计数键。已修复(提交 412e9c48):后端 summary 现返回open_incident_count/healing_total/healing_success(alert_handlers.go:1181-1189),前端别名同步对齐(AlertsPage.vue:904-906),并通过 computed 在缺键时回退 history 统计(AlertsPage.vue:663-680),五卡均正常显示。事件单 Tab 显示「暂无事件单,一切正常。」(历史现象,已修复):旧版 incidents 数据只在「确认/解决写操作成功后」才
fetchIncidents(),首次加载被结构性跳过。已修复(提交 412e9c48):onMounted现并行拉取 incidents(AlertsPage.vue:1401),进入 Tab 即加载;若仍为空说明后端该项目确实无事件单,可用GET /apollo-api/v1/projects/1/incidents核对。切到「自愈策略」Tab 时控制台报错/该卡渲染异常(历史现象,已修复):旧版标题模板引用未声明的
pendingHistory。已修复(提交 412e9c48):待审批计数改为pendingHistoryCount计算属性(AlertsPage.vue:663按history过滤pending_approval,模板:248引用),进入本 Tab 不再抛渲染异常。策略/历史/渠道/静默 Tab 首次进入显示空态(历史现象,已修复):旧版这些列表只在各自写操作成功后 fetch。已修复(提交 412e9c48):
onMounted已并行拉取 policies/history/channels/silences(AlertsPage.vue:1402-1405),进入 Tab 即见存量数据。新建告警规则弹窗的健康检查下拉为空,建了 health_check 规则却从不触发:原因是
fetchHealthChecks()失败被静默降级为空数组(成功才填下拉);或监控侧确实没有健康检查。health_check 类型且health_check_id为空时,EvaluateAll会跳过该规则(其 HealthCheckID 为 nil/0 不参与评估),规则形同虚设。处理:先到「监控可观测」页确认存在且启用的健康检查、检查GET /projects/1/health-checks是否 200(需 PermMonitoringRead);规则弹窗里把下拉选到具体检查再保存。阈值比较规则从不触发或条件摘要异常:原因是 condition.value 空时
Number('')=0(或 NaN 入 JSON 被拒),response_time_ms > 0之类条件一旦结果耗时为 0 便永不命中;或编辑老规则时后端存的 op(=/!=)不在 UI 下拉选项里。处理:新建时填具体阈值;老规则若 op 不在五选项中,先重选一次比较符再保存。另确认metric_source与指标语义一致(health_status 只走健康匹配才有意义)。渠道「测试」提示失败:webhook 会真实 POST(10s 超时),后端错误
Webhook 测试请求失败: ...(URL 不可达/DNS/超时)、Webhook 测试返回非 2xx 状态码: ...(对端拒绝)、解析 webhook 渠道配置失败: .../webhook 渠道缺少 url 配置(config.url 缺失)。email/dingtalk/wecom 恒返回占位成功 note。处理:核对目标 URL 可达性与对端鉴权;URL 填错时到「监控」无关——检查渠道行的 Webhook URL 列展示值。事件单「确认」时填的处置说明没效果:原因是确认接口
handleAcknowledgeIncident只把当前用户名传给Acknowledge,不解析请求体——note 只在「解决」时经Resolve(note)写入resolution_note。处理:需要留痕的说明请在「解决」弹窗里填;确认只用于认领并记录确认人(由后端代码判断,属行为差异非前端 bug,见 8 边界)。新建静默提示「matcher 不是合法 JSON」或后端报「静默结束时间须晚于开始时间」:前端先拦 JSON 语法与时间序,后端再兜底校验。处理:matcher 用合法 JSON 对象(如
{"severity":"critical"});结束时间选在开始时间之后;注意前端new Date(datetime-local).toISOString()会把本地时间按 UTC 序列化,跨时区核对窗口会偏移(同见 8 时间展示项)。
8. 已知缺陷与边界
| 缺陷/边界 | 说明 |
|---|---|
| summary 别名不匹配(已修复) | 已修复(提交 412e9c48):后端 summary 增补自愈计数键返回 open_incident_count/healing_total/healing_success(alert_handlers.go:1181-1189),前端 field 别名同步对齐并新增「自愈成功数」卡(AlertsPage.vue:904-906),五卡正常显示 |
| Tab 内容懒加载缺失(已修复) | 已修复(提交 412e9c48):onMounted 并行拉取 incidents/policies/history/channels/silences(AlertsPage.vue:1397-1407),事件单 Tab 不再结构性恒空 |
pendingHistory 未定义(已修复) | 已修复(提交 412e9c48):改用 pendingHistoryCount 计算属性(AlertsPage.vue:663 按 history 过滤 pending_approval,模板 :248 引用),不再触发渲染异常 |
| 确认弹窗 note 无效(现实边界) | 后端 handleAcknowledgeIncident 只把当前用户名传给 Acknowledge、不解析请求体(server/alert_handlers.go:391-407),确认时填的 note 不落库(仅用户名入 acknowledged_by);note 只在「解决」时经 Resolve(note) 写入 resolution_note(alert_handlers.go:411-434)。前端确认弹窗仍提供 note 输入框,属后端契约边界——需后端按 note 落库才能收口 |
| 项目 id 硬编码 | PROJECT_ID = 1 常量,注释标明「后续可改为从 URL 参数 /project_id 读取」;所有 /projects/1/... 均指向默认项目 |
| 阈值 op 选项不全 | 后端 compareValue 支持 > >= < <= == = !=,UI 下拉仅提供前五(缺 = 与 !=);含 =/!= 的存量规则在编辑弹窗里下拉显示空 |
| event 触发类型缺后端消费 | 规则可选 trigger_type=event,但评估引擎只遍历 health_check 规则;event 类型规则当前无触发源,页面也造不出事件(边界,非缺陷) |
| 无事件列表视图 | GET /projects/1/alert-events 存在,页面「触发事件数」只有计数卡,没有事件明细 Tab;事件明细只能从事件单的 source_alert_ids 反查 |
| 无 investigate 入口 | 后端支持 open→investigating(含审计),页面事件单操作仅确认/解决 |
| 渠道与规则引用软耦合 | 规则按渠道名引用通知渠道,渠道被删后引用悬空,发送时找不到同名渠道即跳过,无前端提示 |
| 自愈执行的真实性 | restart/scale_* 落地为 agent_task(需节点 Agent 执行)、rollback 走 GitOps、clean_disk 为模拟;演示环境无 Agent 时执行结果以 executor 返回为准,页面不做二次核对 |
| approve 无二次确认 | 审批即真实触发动作,页面未做 confirm(deny 有 confirm),误点会立即执行 |
| 时间展示为 UTC | formatTime 对 RFC3339 去 T/Z 截前 19 位展示、不转本地时区;静默窗口 datetime-local→ISO 序列化同样是 UTC 视角,跨时区有偏差 |
| 各 Tab 无轮询/刷新 | onMounted 已并行加载各列表(AlertsPage.vue:1397-1407),此后仅写操作刷新对应列表;页面无定时轮询,长时间停留不会自动看到后端异步触发的新事件单/新历史(自愈/恢复是后端异步动作),需刷新页面或重进 |
附录 A:速查——页面文案与关键常量清单
| 场景 | 源码取值/文案(逐字) |
|---|---|
| 页面标题 | Apollo 告警与自愈 |
| 顶部说明 | 告警规则绑定健康检查(或事件),触发后归并生成事件单;自愈策略按规则自动执行(restart / rollback / scale_up / scale_down / clean_disk),需要审批的策略经人工审批后执行(TAD-09)。 |
| summary 卡标签 | 告警规则数 / 触发事件数 / 未处理事件单 / 自愈执行数 |
| Tab 标签 | 告警规则 / 事件单 / 自愈策略 / 通知渠道 / 静默 |
| 规则列表列头 | ID / 名称 / 级别 / 触发类型 / 指标来源 / 条件 / 通知渠道 / 启用 / 操作 |
| 规则空态 | 暂无告警规则,点击"新建告警规则"添加(项目 #{{ PROJECT_ID }})。 |
| 事件单空态 | 暂无事件单,一切正常。 |
| 策略空态 | 暂无自愈策略,点击"新建自愈策略"添加。 |
| 历史空态 | 暂无自愈历史。 |
| 渠道空态 | 暂无通知渠道,点击"新建通知渠道"添加。 |
| 静默空态 | 暂无静默,点击"新建静默"添加。 |
| 级别枚举 | critical(严重) / warning(警告) / info(提示) |
| 触发类型枚举 | health_check(健康检查) / event(事件) |
| 动作类型枚举 | restart(重启) / rollback(回滚) / scale_up(扩容) / scale_down(缩容) / clean_disk(清理磁盘) |
| 渠道类型枚举 | webhook / email / dingtalk(钉钉) / wecom(企微) |
| 规则名称必填 | 告警规则名称(name)必填 |
| 策略必填提示 | 自愈策略名称与触发规则(trigger_rule_id)必填 |
| 渠道名称必填 | 通知渠道名称(name)必填 |
| 静默校验 | 静默名称与时间窗(starts_at/ends_at)必填 / 结束时间必须晚于开始时间 / matcher 不是合法 JSON:{e.message} |
| 启停提示 | 告警规则 #{id} 已启用/已停用 · 自愈策略 #{id} 已启用/已停用 |
| 删除确认 | 确定删除告警规则 "{name}"(#id)吗? · 确定删除自愈策略 "{name}"(#id)吗? · 确定删除通知渠道 "{name}"(#id)吗? · 确定删除静默 "{name}"(#id)吗? |
| 拒绝确认 | 确定拒绝自愈记录 #{id} 吗?将不再执行该动作。 |
| 创建成功提示 | 告警规则创建成功(id={id}) · 自愈策略创建成功(id={id}) · 通知渠道创建成功(id={id}) · 静默创建成功(id={id}) |
| 事件单处置成功 | 事件单 #{id} 已确认/已解决(状态:{status}) |
| 渠道测试成功 | 通知渠道 "{name}" 测试:{msg}(ok=success/ok/sent 任一 true) |
| 自动关单备注 | 健康检查恢复,告警自动关闭 |
| 事件单标题模板 | 告警规则「{规则名}」触发 |
| 默认常量 | interval=15s(alert 评估)、cooldown/duration 默认 0、max_auto_attempts 默认 1、health_check_id 默认下拉第一项 |
| 阈值 op | UI > >= < <= ==(后端另支持 = !=) |