package handler import ( "context" "fmt" "net/http" "os" "time" "bj_power_mes/common/httpx" "bj_power_mes/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) // diskStatusView 磁盘空间状态视图(附件中心顶部提示 + 定时告警共用)。 type diskStatusView struct { RootDir string `json:"rootDir"` UsedPercent int `json:"usedPercent"` FreeBytes uint64 `json:"freeBytes"` TotalBytes uint64 `json:"totalBytes"` WarnPercent int `json:"warnPercent"` CritPercent int `json:"critPercent"` Level string `json:"level"` // ok / warn / critical / unknown Message string `json:"message"` CheckedAt int64 `json:"checkedAt"` } // diskStatus 读取附件根目录所在磁盘剩余空间并给出告警级别。 // 剩余 < DiskCriticalPercent → critical(提示执行备份归档清理);< DiskWarnPercent → warn。 func diskStatus(svcCtx *svc.ServiceContext) diskStatusView { cfg := svcCtx.Config.Upload root := cfg.Dir if root == "" { root = "uploads" } v := diskStatusView{ RootDir: root, WarnPercent: cfg.DiskWarnPercent, CritPercent: cfg.DiskCriticalPercent, Level: "ok", Message: "磁盘空间正常", CheckedAt: time.Now().Unix(), } used, free, total, err := diskUsage(root) if err != nil { _ = os.MkdirAll(root, 0o755) used, free, total, err = diskUsage(root) } if err != nil { v.Level = "unknown" v.Message = "磁盘空间检测失败:" + err.Error() return v } v.UsedPercent, v.FreeBytes, v.TotalBytes = used, free, total freePercent := 100 - used switch { case freePercent < cfg.DiskCriticalPercent: v.Level = "critical" v.Message = fmt.Sprintf("磁盘剩余空间仅 %d%%,低于 %d%%,请立即执行备份归档清理(附件中心可一键归档)", freePercent, cfg.DiskCriticalPercent) case freePercent < cfg.DiskWarnPercent: v.Level = "warn" v.Message = fmt.Sprintf("磁盘剩余空间 %d%%,低于 %d%% 告警线,请及时清理", freePercent, cfg.DiskWarnPercent) } return v } // DiskStatusHandler GET /attachment/disk-status func DiskStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { httpx.Ok(w, diskStatus(svcCtx)) } } // StartDiskMonitor 启动磁盘空间监控:每天 DiskCheckHour(默认 7 点) 检测一次, // 剩余 <20% 告警、<10% 提示清理(写服务日志 + 操作日志,附件中心顶部同步展示)。 func StartDiskMonitor(svcCtx *svc.ServiceContext) { go func() { hour := svcCtx.Config.Upload.DiskCheckHour if hour < 0 || hour > 23 { hour = 7 } for { now := time.Now() next := time.Date(now.Year(), now.Month(), now.Day(), hour, 0, 0, 0, time.Local) if !next.After(now) { next = next.Add(24 * time.Hour) } time.Sleep(time.Until(next)) checkDiskOnce(svcCtx) } }() } func checkDiskOnce(svcCtx *svc.ServiceContext) { st := diskStatus(svcCtx) switch st.Level { case "critical": logx.Errorf("[磁盘监控] %s(已用 %d%%,剩余 %d%%)", st.Message, st.UsedPercent, 100-st.UsedPercent) svcCtx.EventLog.Write(context.Background(), "system.disk", "", "system", "attachment", "disk", st.Message, map[string]any{"usedPercent": st.UsedPercent, "freeBytes": st.FreeBytes}) case "warn": logx.Alert("[磁盘监控] " + st.Message) svcCtx.EventLog.Write(context.Background(), "system.disk", "", "system", "attachment", "disk", st.Message, map[string]any{"usedPercent": st.UsedPercent, "freeBytes": st.FreeBytes}) default: logx.Infof("[磁盘监控] 正常:剩余 %d%%(%s)", 100-st.UsedPercent, st.RootDir) } }