package storage import ( "encoding/json" "fmt" "log" "time" "mengyaping-backend/config" ) const ( kvKeyServer = "cfg_server" kvKeyMonitor = "cfg_monitor" kvKeyDataPath = "cfg_data_path" kvKeyDatabase = "cfg_database" ) type monitorKVPayload struct { IntervalMinutes int `json:"interval_minutes"` TimeoutSeconds int `json:"timeout_seconds"` RetryCount int `json:"retry_count"` HistoryDays int `json:"history_days"` } func (s *Storage) getKVRaw(key string) string { var row MonitorKV s.db.Where("cfg_key = ?", key).Limit(1).Find(&row) if row.CfgKey == "" { return "" } return row.CfgValue } func (s *Storage) setKVRaw(key, val string) error { var row MonitorKV s.db.Where("cfg_key = ?", key).Limit(1).Find(&row) if row.CfgKey == "" { return s.db.Create(&MonitorKV{CfgKey: key, CfgValue: val}).Error } row.CfgValue = val return s.db.Save(&row).Error } // loadAndSyncAppConfig 从 monitor_kv 覆盖内存配置,再写回全量(补全缺省键、统一格式) func (s *Storage) loadAndSyncAppConfig() { cfg := config.GetConfig() if v := s.getKVRaw(kvKeyServer); v != "" { var sc config.ServerConfig if err := json.Unmarshal([]byte(v), &sc); err != nil { log.Printf("解析 %s: %v", kvKeyServer, err) } else { if sc.Port != "" { cfg.Server.Port = sc.Port } if sc.Host != "" { cfg.Server.Host = sc.Host } } } if v := s.getKVRaw(kvKeyMonitor); v != "" { var m monitorKVPayload if err := json.Unmarshal([]byte(v), &m); err != nil { log.Printf("解析 %s: %v", kvKeyMonitor, err) } else { if m.IntervalMinutes > 0 { snapped := config.SnapMonitorIntervalMinutes(m.IntervalMinutes) cfg.Monitor.Interval = time.Duration(snapped) * time.Minute } if m.TimeoutSeconds > 0 { cfg.Monitor.Timeout = time.Duration(m.TimeoutSeconds) * time.Second } if m.RetryCount > 0 { cfg.Monitor.RetryCount = m.RetryCount } if m.HistoryDays > 0 { cfg.Monitor.HistoryDays = m.HistoryDays } } } if v := s.getKVRaw(kvKeyDataPath); v != "" { cfg.DataPath = v } if v := s.getKVRaw(kvKeyDatabase); v != "" { var dc config.DatabaseConfig if err := json.Unmarshal([]byte(v), &dc); err != nil { log.Printf("解析 %s: %v", kvKeyDatabase, err) } else { if dc.Host != "" { cfg.Database.Host = dc.Host } if dc.Port != "" { cfg.Database.Port = dc.Port } if dc.User != "" { cfg.Database.User = dc.User } if dc.Password != "" { cfg.Database.Password = dc.Password } if dc.Database != "" { cfg.Database.Database = dc.Database } } } if err := s.PersistAppConfig(); err != nil { log.Printf("同步应用配置到 monitor_kv: %v", err) } } // PersistAppConfig 将当前内存中的 server/monitor/data_path/database 写入 monitor_kv func (s *Storage) PersistAppConfig() error { cfg := config.GetConfig() b, err := json.Marshal(cfg.Server) if err != nil { return err } if err := s.setKVRaw(kvKeyServer, string(b)); err != nil { return err } m := monitorKVPayload{ IntervalMinutes: config.SnapMonitorIntervalMinutes(int(cfg.Monitor.Interval / time.Minute)), TimeoutSeconds: int(cfg.Monitor.Timeout.Seconds()), RetryCount: cfg.Monitor.RetryCount, HistoryDays: cfg.Monitor.HistoryDays, } b, err = json.Marshal(m) if err != nil { return err } if err := s.setKVRaw(kvKeyMonitor, string(b)); err != nil { return err } if err := s.setKVRaw(kvKeyDataPath, cfg.DataPath); err != nil { return err } b, err = json.Marshal(cfg.Database) if err != nil { return err } return s.setKVRaw(kvKeyDatabase, string(b)) } // SetMonitorIntervalMinutes 更新检测周期并写入 monitor_kv func (s *Storage) SetMonitorIntervalMinutes(minutes int) error { if !config.IsAllowedMonitorInterval(minutes) { return fmt.Errorf("interval_minutes 必须是预设值之一: %v", config.AllowedMonitorIntervalMinutes) } cfg := config.GetConfig() cfg.Monitor.Interval = time.Duration(minutes) * time.Minute return s.PersistAppConfig() }