本周我主要做的工作不是单独开发某一个页面,而是围绕“接口层”去整理和思考:现有后端到底能提供什么能力,前端应该怎么接,哪些字段需要映射,哪些接口还需要补充。

我觉得这类工作虽然不如页面效果直观,但它决定了一个项目能不能真正从“前端原型 + 后端原型”变成“能够联动的系统”。

1. 项目现状

项目实际上已经有两个基础部分:

一个基于 FastAPI 的后端原型
一个基于 Vue 3 的前端监控界面
前端入口文件比较标准,使用了 Vue Router、Pinia、Element Plus 和 ECharts:

import { createApp } from 'vue'
import App from './App.vue'
import router, { setupRouterGuard } from './router'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import ECharts from 'vue-echarts'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { LineChart, BarChart } from 'echarts/charts'
import { GridComponent, TooltipComponent, LegendComponent, TitleComponent } from 'echarts/components'

use([CanvasRenderer, LineChart, BarChart, GridComponent, TooltipComponent, LegendComponent, TitleComponent])

const app = createApp(App)
const pinia = createPinia()

app.use(router)
app.use(pinia)
setupRouterGuard(pinia)

for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

app.use(ElementPlus)
app.component('v-chart', ECharts)

app.mount('#app')

也就是说,前端结构已经有了,页面也已经分成了 Dashboard、Alerts、Logs、Chat 等模块。
问题不在“有没有前端”,而在于“前端想调用的接口”和“后端实际提供的接口”并不一致。

2. 后端核心能力

我先梳理了后端到底已经实现了哪些能力。

2.1 健康检查接口


第一个基础接口是健康检查:

@app.get("/api/v1/health")
async def health_check():
    """健康检查"""
    return {"status": "ok", "version": "0.1.0"}

这个接口的作用很简单,但对前端很有用。
前端可以在页面初始化时调用它,判断后端是否在线。

2.2 告警历史接口


第二个接口是告警历史:

@app.get("/api/v1/alerts/history")
async def get_alert_history():
    """获取已接收的告警历史"""
    return {
        "total": len(receiver.received_alerts),
        "alerts": [a.to_dict() for a in receiver.received_alerts[-10:]]  # 最近10条
    }

这个接口负责返回最近接收到的告警。
虽然目前只是保存在内存中,没有接数据库,但已经足够支撑前端做“最近告警列表”的展示。

2.3 告警接收与分析接口


最核心的接口是告警接收接口:

@app.post("/api/v1/alerts/receive")
async def receive_alert(request: Request):
    """
    接收Alertmanager webhook推送
    """
    payload = await request.json()
    alerts = receiver.parse_webhook(payload)
    
    results = []
    for alert in alerts:
        # 检索知识库
        matches = kb.search(alert, top_k=2)
        
        # 根因分析
        analysis = analyzer.analyze(alert, matches)
        
        results.append({
            "alert": alert.to_dict(),
            "knowledge_matches": [
                {"id": m.entry["id"], "title": m.entry["title"], "score": m.match_score}
                for m in matches
            ],
            "root_cause": {
                "candidates": analysis.candidate_causes,
                "confidence": analysis.confidence,
                "remediation": analysis.remediation
            }
        })
    
    return JSONResponse({
        "status": "processed",
        "count": len(results),
        "results": results
    })

这个接口其实已经把整个后端主链路打通了:

接收告警 JSON
调用 receiver.parse_webhook(payload) 解析原始告警
调用 kb.search(alert, top_k=2) 做知识库检索
调用 analyzer.analyze(alert, matches) 生成分析结果
返回给前端结构化结果
从接口设计的角度看,这已经是一个很适合演示的后端能力。

3. 告警解析模块给前端提供的了哪些字段


在后端中,告警解析逻辑主要在 alert_receiver.py 里。
它会把 webhook 里的原始告警解析成统一结构:

parsed = ParsedAlert(
    alert_id=f"{labels.get('alertname', 'unknown')}-{alert.get('startsAt', '')}",
    status=alert.get("status", "firing"),
    severity=labels.get("severity", "P3"),
    service=self._normalize_label(labels, "service"),
    instance=self._normalize_label(labels, "instance"),
    alertname=labels.get("alertname", "unknown"),
    summary=annotations.get("summary", ""),
    description=annotations.get("description", ""),
    starts_at=alert.get("startsAt", ""),
    generator_url=alert.get("generatorURL", ""),
    raw_labels=labels,
    raw_annotations=annotations
)

然后通过 to_dict() 返回给接口层:

def to_dict(self) -> Dict[str, Any]:
    return {
        "alert_id": self.alert_id,
        "status": self.status,
        "severity": self.severity,
        "service": self.service,
        "instance": self.instance,
        "alertname": self.alertname,
        "summary": self.summary,
        "description": self.description,
        "starts_at": self.starts_at,
        "generator_url": self.generator_url
    }

这一部分是我在做前后端整合时特别关注的,因为它直接决定了前端实际能拿到哪些字段。

4. 知识库和分析结果之间的组织


后端不仅返回告警本身,还会返回知识匹配和根因分析。
知识库检索模块本质上是根据告警名、症状和指标做匹配:

matches = kb.search(alert, top_k=2)

分析模块则返回类似这样的结构:

{
    "root_cause": {
        "candidates": analysis.candidate_causes,
        "confidence": analysis.confidence,
        "remediation": analysis.remediation
    }
}

这个结果对前端很有价值,因为前端不再只是展示“有一条告警”,还可以展示:

可能的根因是什么
置信度是多少
建议立刻采取哪些处理措施
从产品角度来看,这也是这个项目区别于普通监控面板的地方。

5.我在整合时遇到的核心问题


5.1 前端预期接口与后端实际接口不一致


前端目前的请求封装在 api/request.js 中:

import axios from 'axios'
import { ElMessage } from 'element-plus'

const service = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:5000/api',
  timeout: 15000
})

这里默认的后端地址是 http://127.0.0.1:5000/api,但我现有的 FastAPI 后端实际上跑在 8080 端口,并且接口路径是 /api/v1/...。

另外,前端定义的监控接口是:

import request from './request'

// 获取主机列表及实时状态
export const getHostList = () => request.get('/hosts')

// 获取单个主机的监控指标,例如 CPU、内存等
export const getHostMetrics = (hostId, timeRange = '1h') =>
  request.get(`/metrics/${hostId}`, { params: { range: timeRange } })

// 获取告警列表
export const getAlerts = () => request.get('/alerts')

但当前后端并没有实现:

/hosts
/metrics/:hostId
/alerts
它只有:

/api/v1/health
/api/v1/alerts/history
/api/v1/alerts/receive
所以我的一个核心判断是:
这个阶段不能简单地“让前端照旧去调后端”,而是必须先统一接口设计。

5.2 前端字段与后端字段不一致


前端 Alerts.vue 需要的字段更偏页面展示:

host
source
message
timestamp
status
而后端返回的是:

instance
alertname
summary
starts_at
status
所以我重点考虑的是如何做字段映射,而不是让前端页面大改。

我的映射思路是:

id <- alert_id
timestamp <- starts_at
host <- instance
source <- alertname
message <- summary || description
status <- status
这样前端仍然可以保持原本的表格结构,只需要在数据层做一次转换。

6. 为当前最合理的接入方式

6.1 Dashboard 页面


Dashboard 不必一开始就做成完整主机监控页,而是可以先改成“系统概览页”,展示:

后端是否在线
最近告警数
最近一条告警摘要
它主要依赖两个接口:

GET /api/v1/health
GET /api/v1/alerts/history


6.2 Alerts 页面


Alerts 是最值得优先接通的页面。
因为它和现有后端能力最契合。

一方面,它可以调用:

GET /api/v1/alerts/history

获取最近告警列表。

另一方面,它还可以增加一个“测试分析”按钮,调用:

POST /api/v1/alerts/receive

提交一条测试告警,然后把返回的分析结果显示出来。

这样就能真正展示项目最核心的能力:
告警接收 + 根因分析 + 前端展示

6.3 Logs 和 Chat 页面


这两个页面目前后端并没有对应的真实接口。
所以从整合顺序上,可以先保留 Logs.vue 的 mock 数据和 Chat.vue 的 mock 问答
等核心链路接通之后,再决定要不要继续扩展真实日志接口或智能问答接口。

7. 对于接口端的思考与总结


这次整理下来,我觉得自己做的接口端工作主要有三层。

第一层:确认后端真正已经具备哪些能力

第二层:把前后端的数据结构对齐

第三层:确定分阶段接入策略

这次在 OpsPilot 项目中,我主要做了这些工作:

梳理后端核心接口
分析后端返回的数据结构
识别前端与后端之间的不匹配点
给出字段映射方案
明确页面接入优先级

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐