feat: 监控告警体系统一重构 + 自动化任务调度引擎 + 监控大屏拆分

告警中心
- 新增统一告警(alert_unified)、监控告警规则(monitor_alert_rule)、指标目录(alert_metric_catalog)
- 通知策略与消息分发(alert_notify_policy/dispatch/message)、静默规则
- 告警引擎按阈值/SNMP 拆分(alert_engine_monitor/snmp)

监控采集
- SNMP 常量/模板与历史指标(snmp templates/constants、snmp_metrics_history、snmp_collect)
- 中间件监控 Redis 采集与种子(middleware_monitor_redis/seed)
- 站点探测 pkg(siteprobe) + 站点视图(site_view)

自动化任务
- cron 调度器(cronscheduler)、脚本运行器(scriptrunner)、系统任务(systask)及运行记录(cron_job_run、system_task)

审计与凭证
- 审计设置(audit_setting) + 历史清理(auditcleaner)

前端
- 监控页面(host/middleware/site/snmp)拆分为子模块并大屏化
- 告警中心多 Tab(规则/指标目录/通知策略/静默)、任务管理组件重构、Agent CLI Markdown 渲染
- 移除旧脚本卡片/详情/日志组件,统一为 ScriptTable/ScriptCodeEditor/TerminalOutput

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
LoganandClaude committed 2026-07-01 23:44:45 +08:00
1 parent 092d0e4b9e
commit 471353e3d7
190 files changed
+16640 -3484

No files matched your search

+138
View File
@@ -0,0 +1,138 @@
// 用户定时任务调度 — Linux 标准 5 段 crontab(分 时 日 月 周)// Logan.Li
package cronscheduler
import (
"log"
"strings"
"sync"
"time"
"ezops/model"
"github.com/robfig/cron/v3"
"gorm.io/gorm"
)
// Runner 执行定时任务回调(由 controller 注入)// Logan.Li
type Runner func(jobID uint)
type trackedEntry struct {
entryID cron.EntryID
expr string
}
// Scheduler 后台 Cron 调度器 // Logan.Li
type Scheduler struct {
db *gorm.DB
runner Runner
cron *cron.Cron
mu sync.Mutex
entries map[uint]trackedEntry
running sync.Map
}
// Start 启动调度器:加载已启用任务,并周期性同步 DB 变更 // Logan.Li
func Start(db *gorm.DB, runner Runner) *Scheduler {
s := &Scheduler{
db: db,
runner: runner,
cron: cron.New(cron.WithLocation(time.Local)),
entries: make(map[uint]trackedEntry),
}
s.cron.Start()
s.reload()
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for range ticker.C {
s.reload()
}
}()
log.Println("[CronScheduler] 已启动(Linux 5 段表达式:分 时 日 月 周)")
return s
}
func (s *Scheduler) Stop() {
s.cron.Stop()
}
func (s *Scheduler) reload() {
var jobs []model.CronJob
if err := s.db.Where("enabled = ?", true).Find(&jobs).Error; err != nil {
log.Printf("[CronScheduler] 加载任务失败: %v", err)
return
}
s.mu.Lock()
defer s.mu.Unlock()
active := make(map[uint]struct{}, len(jobs))
for _, job := range jobs {
expr := normalizeCronExpr(job.CronExpr)
if expr == "" {
if prev, ok := s.entries[job.ID]; ok {
s.cron.Remove(prev.entryID)
delete(s.entries, job.ID)
}
continue
}
active[job.ID] = struct{}{}
if prev, ok := s.entries[job.ID]; ok && prev.expr == expr {
continue
}
if prev, ok := s.entries[job.ID]; ok {
s.cron.Remove(prev.entryID)
}
jobID := job.ID
entryID, err := s.cron.AddFunc(expr, func() {
s.dispatch(jobID)
})
if err != nil {
log.Printf("[CronScheduler] 注册任务 %s(%d) 失败 expr=%q: %v", job.Name, job.ID, expr, err)
delete(s.entries, job.ID)
continue
}
s.entries[job.ID] = trackedEntry{entryID: entryID, expr: expr}
log.Printf("[CronScheduler] 已注册: %s id=%d expr=%s", job.Name, job.ID, expr)
}
for id, prev := range s.entries {
if _, ok := active[id]; !ok {
s.cron.Remove(prev.entryID)
delete(s.entries, id)
log.Printf("[CronScheduler] 已移除: id=%d", id)
}
}
}
func (s *Scheduler) dispatch(jobID uint) {
if _, loaded := s.running.LoadOrStore(jobID, true); loaded {
log.Printf("[CronScheduler] 任务 id=%d 仍在执行,跳过本次调度", jobID)
return
}
defer s.running.Delete(jobID)
if s.runner == nil {
return
}
s.runner(jobID)
}
// normalizeCronExpr 规整为 Linux crontab 五段式 // Logan.Li
func normalizeCronExpr(expr string) string {
expr = strings.TrimSpace(expr)
if expr == "" {
return ""
}
fields := strings.Fields(expr)
if len(fields) == 5 {
return strings.Join(fields, " ")
}
// 误填 6 段(含秒)时去掉首段,兼容常见 Quartz 写法 // Logan.Li
if len(fields) == 6 {
return strings.Join(fields[1:], " ")
}
return ""
}