Files
aiops/backend/controller/alert_rules.go
T
attacker 894cb16a05 feat(monitor): F5 链路追踪后端对接 — traceApi mock桩改真实Jaeger/Tempo代理+demo兜底
- 后端新增 monitor_trace_datasources 表 + TraceDataSource 模型 (Jaeger/Tempo)
- controller/trace_datasource.go — CRUD + TestConnection + SetDefault (7 路由)
- controller/trace_proxy.go — Jaeger API 代理 + 响应格式转换 (Trace/服务/Span)
  - GET /traces — Trace 列表 (代理 Jaeger /api/traces)
  - GET /traces/services — 服务列表 + P95/P99/errorRate 聚合
  - GET /traces/:traceId — Trace 详情 (Span 树转换)
- Demo 数据兜底: 无数据源时返回模拟数据 (6 Trace + 5 服务 + 5 Span)
- 前端 traceApi 从 mock 改真实 HTTP 调用
- 前端新增 trace-datasource.ts 数据源管理 API
- ROADMAP/TDD_PLAN/PROJECT_EVALUATION 文档同步更新
- 综合评分 90→91, 功能完整性 92→94 (mock API 清零)
2026-06-28 01:51:00 +08:00

341 lines
8.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package controller
import (
"fmt"
"net/http"
"strconv"
"ezops/model"
"github.com/gin-gonic/gin"
)
// 告警规则 CRUD(AlertHandler 方法接收者,与 alert.go 同包共享) // Logan.Li
// ListRules 获取告警规则列表 // Logan.Li
// @Summary 获取告警规则列表
// @Description 获取所有告警规则列表
// @Tags 告警
// @Produce json
// @Security Bearer
// @Param page query int false "页码"
// @Param page_size query int false "每页数量"
// @Success 200 {object} model.Response{data=model.ListResponse}
// @Router /alerts/rules [get]
func (h *AlertHandler) ListRules(c *gin.Context) {
var query model.PageQuery
c.ShouldBindQuery(&query)
var total int64
h.db.Model(&model.AlertRule{}).Count(&total)
var rules []model.AlertRule
h.db.Offset(query.GetOffset()).Limit(query.GetLimit()).Find(&rules)
c.JSON(http.StatusOK, model.Response{
Code: 200,
Message: "success",
Data: model.ListResponse{
List: rules,
Total: total,
},
})
}
// CreateRule 创建告警规则 // Logan.Li
// @Summary 创建告警规则
// @Description 创建新告警规则
// @Tags 告警
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body model.CreateAlertRuleRequest true "创建请求"
// @Success 200 {object} model.Response{data=model.AlertRule}
// @Router /alerts/rules [post]
func (h *AlertHandler) CreateRule(c *gin.Context) {
var req model.CreateAlertRuleRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, model.Response{
Code: 400,
Message: "请求参数错误: " + err.Error(),
})
return
}
rule := model.AlertRule{
Name: req.Name,
Description: req.Description,
Type: req.Type,
Source: req.Source,
Expression: req.Expression,
Severity: req.Severity,
Duration: req.Duration,
DatasourceID: req.DatasourceID,
Labels: req.Labels,
Annotations: req.Annotations,
NotifyChannels: toJSON(req.NotifyChannels),
Enabled: true,
}
if err := h.db.Create(&rule).Error; err != nil {
c.JSON(http.StatusInternalServerError, model.Response{
Code: 500,
Message: "创建失败",
})
return
}
c.JSON(http.StatusOK, model.Response{
Code: 200,
Message: "创建成功",
Data: rule,
})
}
// UpdateRule 更新告警规则 // Logan.Li
// @Summary 更新告警规则
// @Description 更新指定告警规则
// @Tags 告警
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path int true "规则ID"
// @Param request body model.UpdateAlertRuleRequest true "更新请求"
// @Success 200 {object} model.Response{data=model.AlertRule}
// @Router /alerts/rules/{id} [put]
func (h *AlertHandler) UpdateRule(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
var req model.UpdateAlertRuleRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, model.Response{
Code: 400,
Message: "请求参数错误: " + err.Error(),
})
return
}
var rule model.AlertRule
if err := h.db.First(&rule, id).Error; err != nil {
c.JSON(http.StatusNotFound, model.Response{
Code: 404,
Message: "规则不存在",
})
return
}
updates := map[string]interface{}{}
if req.Name != "" {
updates["name"] = req.Name
}
if req.Description != "" {
updates["description"] = req.Description
}
if req.Expression != "" {
updates["expression"] = req.Expression
}
if req.Severity != "" {
updates["severity"] = req.Severity
}
if req.Duration > 0 {
updates["duration"] = req.Duration
}
if req.DatasourceID > 0 {
updates["datasource_id"] = req.DatasourceID
}
if req.Labels != "" {
updates["labels"] = req.Labels
}
if req.Annotations != "" {
updates["annotations"] = req.Annotations
}
if len(req.NotifyChannels) > 0 {
updates["notify_channels"] = toJSON(req.NotifyChannels)
}
if req.Enabled != nil {
updates["enabled"] = *req.Enabled
}
if err := h.db.Model(&rule).Updates(updates).Error; err != nil {
c.JSON(http.StatusInternalServerError, model.Response{
Code: 500,
Message: "更新失败",
})
return
}
h.db.First(&rule, id)
c.JSON(http.StatusOK, model.Response{
Code: 200,
Message: "更新成功",
Data: rule,
})
}
// DeleteRule 删除告警规则 // Logan.Li
// @Summary 删除告警规则
// @Description 删除指定告警规则
// @Tags 告警
// @Produce json
// @Security Bearer
// @Param id path int true "规则ID"
// @Success 200 {object} model.Response
// @Router /alerts/rules/{id} [delete]
func (h *AlertHandler) DeleteRule(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
if err := h.db.Delete(&model.AlertRule{}, id).Error; err != nil {
c.JSON(http.StatusInternalServerError, model.Response{
Code: 500,
Message: "删除失败",
})
return
}
c.JSON(http.StatusOK, model.Response{
Code: 200,
Message: "删除成功",
})
}
// BatchDeleteRules 批量删除告警规则 // Logan.Li
// @Summary 批量删除告警规则
// @Description 批量删除指定告警规则
// @Tags 告警
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body model.IDsRequest true "删除请求"
// @Success 200 {object} model.Response
// @Router /alerts/rules/batch-delete [delete]
func (h *AlertHandler) BatchDeleteRules(c *gin.Context) {
var req model.IDsRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, model.Response{
Code: 400,
Message: "请求参数错误",
})
return
}
if len(req.IDs) == 0 {
c.JSON(http.StatusBadRequest, model.Response{
Code: 400,
Message: "请选择要删除的规则",
})
return
}
if err := h.db.Delete(&model.AlertRule{}, req.IDs).Error; err != nil {
c.JSON(http.StatusInternalServerError, model.Response{
Code: 500,
Message: "删除失败",
})
return
}
c.JSON(http.StatusOK, model.Response{
Code: 200,
Message: "删除成功",
})
}
// ToggleRule 切换告警规则状态 // Logan.Li
// @Summary 切换告警规则状态
// @Description 启用或禁用告警规则
// @Tags 告警
// @Produce json
// @Security Bearer
// @Param id path int true "规则ID"
// @Success 200 {object} model.Response{data=model.AlertRule}
// @Router /alerts/rules/{id}/toggle [post]
func (h *AlertHandler) ToggleRule(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
var rule model.AlertRule
if err := h.db.First(&rule, id).Error; err != nil {
c.JSON(http.StatusNotFound, model.Response{
Code: 404,
Message: "规则不存在",
})
return
}
rule.Enabled = !rule.Enabled
if err := h.db.Save(&rule).Error; err != nil {
c.JSON(http.StatusInternalServerError, model.Response{
Code: 500,
Message: "操作失败",
})
return
}
c.JSON(http.StatusOK, model.Response{
Code: 200,
Message: "操作成功",
Data: rule,
})
}
// QuickSetupRules 一键创建测试告警规则(飞书Webhook测试用) // Logan.Li
// @Summary 一键创建测试告警规则
// @Tags 告警
// @Router /alerts/quick-setup [post]
func (h *AlertHandler) QuickSetupRules(c *gin.Context) {
type ruleDef struct {
Name string `json:"name"`
Metric string `json:"metric"`
Operator string `json:"operator"`
Threshold float64 `json:"threshold"`
Port int `json:"port,omitempty"`
}
var req struct {
Rules []ruleDef `json:"rules"`
}
c.ShouldBindJSON(&req)
// 默认规则:CPU 80%, 内存 80%, 磁盘 90%, TIME_WAIT 15000, CLOSE_WAIT 200, 端口Down, 进程检测
defaultRules := []ruleDef{
{Name: "CPU使用率", Metric: "cpu", Operator: ">", Threshold: 80},
{Name: "内存使用率", Metric: "mem", Operator: ">", Threshold: 80},
{Name: "磁盘使用率", Metric: "disk", Operator: ">", Threshold: 90},
{Name: "TIME_WAIT连接数", Metric: "tcp_time_wait", Operator: ">", Threshold: 15000},
{Name: "CLOSE_WAIT连接数", Metric: "tcp_close_wait", Operator: ">", Threshold: 200},
{Name: "进程异常退出", Metric: "service_process", Operator: "==", Threshold: 0},
}
rules := req.Rules
if len(rules) == 0 {
rules = defaultRules
}
created := 0
for _, def := range rules {
var existing model.AgentRule
h.db.Where("name = ?", def.Name).First(&existing)
if existing.ID > 0 {
continue
}
rule := model.AgentRule{
Name: def.Name,
Metric: def.Metric,
Operator: def.Operator,
Threshold: def.Threshold,
Port: def.Port,
Enabled: true,
}
if err := h.db.Create(&rule).Error; err != nil {
continue
}
created++
}
c.JSON(http.StatusOK, model.Response{
Code: 200,
Message: fmt.Sprintf("已创建 %d 条告警规则", created),
Data: gin.H{"created": created},
})
}