feat: Implement a new monitoring system with auto-add capabilities and a dedicated management UI.
This commit is contained in:
@@ -34,15 +34,24 @@ func StartAutoAddService() {
|
|||||||
logger.Info("自动补号检查器已启动(需在前端开启开关)", "", "auto-add")
|
logger.Info("自动补号检查器已启动(需在前端开启开关)", "", "auto-add")
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ticker := time.NewTicker(60 * time.Second) // 每分钟检查一次
|
// 默认检查间隔 60 秒
|
||||||
defer ticker.Stop()
|
checkInterval := 60
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
// 动态读取检查间隔配置
|
||||||
|
if database.Instance != nil {
|
||||||
|
if val, _ := database.Instance.GetConfig("monitor_check_interval"); val != "" {
|
||||||
|
if v, err := strconv.Atoi(val); err == nil && v >= 10 {
|
||||||
|
checkInterval = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-autoAddStopChan:
|
case <-autoAddStopChan:
|
||||||
logger.Info("自动补号检查器已停止", "", "auto-add")
|
logger.Info("自动补号检查器已停止", "", "auto-add")
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-time.After(time.Duration(checkInterval) * time.Second):
|
||||||
checkAndAutoAdd()
|
checkAndAutoAdd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,6 +81,7 @@ func checkAndAutoAdd() {
|
|||||||
autoAddEnabled = true
|
autoAddEnabled = true
|
||||||
}
|
}
|
||||||
if !autoAddEnabled {
|
if !autoAddEnabled {
|
||||||
|
// 自动补号未开启,静默返回
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +93,11 @@ func checkAndAutoAdd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Since(lastAutoAddTime).Seconds() < float64(minInterval) {
|
elapsed := time.Since(lastAutoAddTime).Seconds()
|
||||||
|
if elapsed < float64(minInterval) {
|
||||||
|
// 距离上次补号时间不足,显示等待信息
|
||||||
|
remaining := float64(minInterval) - elapsed
|
||||||
|
logger.Info(fmt.Sprintf("自动补号: 等待中 (还需 %.0f 秒)", remaining), "", "auto-add")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +124,7 @@ func checkAndAutoAdd() {
|
|||||||
|
|
||||||
deficit := target - current
|
deficit := target - current
|
||||||
if deficit <= 0 {
|
if deficit <= 0 {
|
||||||
|
logger.Info(fmt.Sprintf("自动补号: 当前 %d >= 目标 %d, 无需补号", current, target), "", "auto-add")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type MonitorSettings struct {
|
|||||||
Target int `json:"target"`
|
Target int `json:"target"`
|
||||||
AutoAdd bool `json:"auto_add"`
|
AutoAdd bool `json:"auto_add"`
|
||||||
MinInterval int `json:"min_interval"`
|
MinInterval int `json:"min_interval"`
|
||||||
|
CheckInterval int `json:"check_interval"` // 自动补号检查间隔(秒)
|
||||||
PollingEnabled bool `json:"polling_enabled"`
|
PollingEnabled bool `json:"polling_enabled"`
|
||||||
PollingInterval int `json:"polling_interval"`
|
PollingInterval int `json:"polling_interval"`
|
||||||
}
|
}
|
||||||
@@ -34,6 +35,7 @@ func HandleGetMonitorSettings(w http.ResponseWriter, r *http.Request) {
|
|||||||
Target: 50,
|
Target: 50,
|
||||||
AutoAdd: false,
|
AutoAdd: false,
|
||||||
MinInterval: 300,
|
MinInterval: 300,
|
||||||
|
CheckInterval: 60,
|
||||||
PollingEnabled: false,
|
PollingEnabled: false,
|
||||||
PollingInterval: 60,
|
PollingInterval: 60,
|
||||||
}
|
}
|
||||||
@@ -51,6 +53,11 @@ func HandleGetMonitorSettings(w http.ResponseWriter, r *http.Request) {
|
|||||||
settings.MinInterval = v
|
settings.MinInterval = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if val, _ := database.Instance.GetConfig("monitor_check_interval"); val != "" {
|
||||||
|
if v, err := strconv.Atoi(val); err == nil {
|
||||||
|
settings.CheckInterval = v
|
||||||
|
}
|
||||||
|
}
|
||||||
if val, _ := database.Instance.GetConfig("monitor_polling_enabled"); val == "true" {
|
if val, _ := database.Instance.GetConfig("monitor_polling_enabled"); val == "true" {
|
||||||
settings.PollingEnabled = true
|
settings.PollingEnabled = true
|
||||||
}
|
}
|
||||||
@@ -85,6 +92,7 @@ func HandleSaveMonitorSettings(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
logger.Info("收到保存监控设置请求: target="+strconv.Itoa(settings.Target)+
|
logger.Info("收到保存监控设置请求: target="+strconv.Itoa(settings.Target)+
|
||||||
", auto_add="+strconv.FormatBool(settings.AutoAdd)+
|
", auto_add="+strconv.FormatBool(settings.AutoAdd)+
|
||||||
|
", check_interval="+strconv.Itoa(settings.CheckInterval)+
|
||||||
", polling="+strconv.FormatBool(settings.PollingEnabled), "", "monitor")
|
", polling="+strconv.FormatBool(settings.PollingEnabled), "", "monitor")
|
||||||
|
|
||||||
// 保存到数据库
|
// 保存到数据库
|
||||||
@@ -98,6 +106,14 @@ func HandleSaveMonitorSettings(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err := database.Instance.SetConfig("monitor_min_interval", strconv.Itoa(settings.MinInterval)); err != nil {
|
if err := database.Instance.SetConfig("monitor_min_interval", strconv.Itoa(settings.MinInterval)); err != nil {
|
||||||
saveErrors = append(saveErrors, "min_interval: "+err.Error())
|
saveErrors = append(saveErrors, "min_interval: "+err.Error())
|
||||||
}
|
}
|
||||||
|
// 检查间隔最小10秒
|
||||||
|
checkInterval := settings.CheckInterval
|
||||||
|
if checkInterval < 10 {
|
||||||
|
checkInterval = 10
|
||||||
|
}
|
||||||
|
if err := database.Instance.SetConfig("monitor_check_interval", strconv.Itoa(checkInterval)); err != nil {
|
||||||
|
saveErrors = append(saveErrors, "check_interval: "+err.Error())
|
||||||
|
}
|
||||||
if err := database.Instance.SetConfig("monitor_polling_enabled", strconv.FormatBool(settings.PollingEnabled)); err != nil {
|
if err := database.Instance.SetConfig("monitor_polling_enabled", strconv.FormatBool(settings.PollingEnabled)); err != nil {
|
||||||
saveErrors = append(saveErrors, "polling_enabled: "+err.Error())
|
saveErrors = append(saveErrors, "polling_enabled: "+err.Error())
|
||||||
}
|
}
|
||||||
@@ -115,8 +131,9 @@ func HandleSaveMonitorSettings(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 输出日志
|
// 输出日志
|
||||||
logger.Success("监控设置已保存: target="+strconv.Itoa(settings.Target)+
|
logger.Success("监控设置已保存: target="+strconv.Itoa(settings.Target)+
|
||||||
", auto_add="+strconv.FormatBool(settings.AutoAdd)+
|
", auto_add="+strconv.FormatBool(settings.AutoAdd)+
|
||||||
|
", check_interval="+strconv.Itoa(checkInterval)+"s"+
|
||||||
", polling="+strconv.FormatBool(settings.PollingEnabled)+
|
", polling="+strconv.FormatBool(settings.PollingEnabled)+
|
||||||
", interval="+strconv.Itoa(settings.PollingInterval)+"s", "", "monitor")
|
", polling_interval="+strconv.Itoa(settings.PollingInterval)+"s", "", "monitor")
|
||||||
|
|
||||||
Success(w, map[string]interface{}{
|
Success(w, map[string]interface{}{
|
||||||
"message": "设置已保存",
|
"message": "设置已保存",
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export default function Monitor() {
|
|||||||
const [targetInput, setTargetInput] = useState(50)
|
const [targetInput, setTargetInput] = useState(50)
|
||||||
const [autoAdd, setAutoAdd] = useState(false)
|
const [autoAdd, setAutoAdd] = useState(false)
|
||||||
const [minInterval, setMinInterval] = useState(300)
|
const [minInterval, setMinInterval] = useState(300)
|
||||||
|
const [checkInterval, setCheckInterval] = useState(60)
|
||||||
const [pollingEnabled, setPollingEnabled] = useState(false)
|
const [pollingEnabled, setPollingEnabled] = useState(false)
|
||||||
const [pollingInterval, setPollingInterval] = useState(60)
|
const [pollingInterval, setPollingInterval] = useState(60)
|
||||||
|
|
||||||
@@ -125,6 +126,7 @@ export default function Monitor() {
|
|||||||
target: targetInput,
|
target: targetInput,
|
||||||
auto_add: autoAdd,
|
auto_add: autoAdd,
|
||||||
min_interval: minInterval,
|
min_interval: minInterval,
|
||||||
|
check_interval: checkInterval,
|
||||||
polling_enabled: pollingEnabled,
|
polling_enabled: pollingEnabled,
|
||||||
polling_interval: pollingInterval,
|
polling_interval: pollingInterval,
|
||||||
}),
|
}),
|
||||||
@@ -169,6 +171,7 @@ export default function Monitor() {
|
|||||||
target: targetInput,
|
target: targetInput,
|
||||||
auto_add: autoAdd,
|
auto_add: autoAdd,
|
||||||
min_interval: minInterval,
|
min_interval: minInterval,
|
||||||
|
check_interval: checkInterval,
|
||||||
polling_enabled: newPollingEnabled,
|
polling_enabled: newPollingEnabled,
|
||||||
polling_interval: pollingInterval,
|
polling_interval: pollingInterval,
|
||||||
}),
|
}),
|
||||||
@@ -195,6 +198,7 @@ export default function Monitor() {
|
|||||||
target: targetInput,
|
target: targetInput,
|
||||||
auto_add: autoAdd,
|
auto_add: autoAdd,
|
||||||
min_interval: minInterval,
|
min_interval: minInterval,
|
||||||
|
check_interval: checkInterval,
|
||||||
polling_enabled: pollingEnabled,
|
polling_enabled: pollingEnabled,
|
||||||
polling_interval: pollingInterval,
|
polling_interval: pollingInterval,
|
||||||
}),
|
}),
|
||||||
@@ -257,19 +261,21 @@ export default function Monitor() {
|
|||||||
const target = s.target || 50
|
const target = s.target || 50
|
||||||
const autoAddVal = s.auto_add || false
|
const autoAddVal = s.auto_add || false
|
||||||
const minIntervalVal = s.min_interval || 300
|
const minIntervalVal = s.min_interval || 300
|
||||||
|
const checkIntervalVal = s.check_interval || 60
|
||||||
const pollingEnabledVal = s.polling_enabled || false
|
const pollingEnabledVal = s.polling_enabled || false
|
||||||
const interval = s.polling_interval || 60
|
const interval = s.polling_interval || 60
|
||||||
|
|
||||||
setTargetInput(target)
|
setTargetInput(target)
|
||||||
setAutoAdd(autoAddVal)
|
setAutoAdd(autoAddVal)
|
||||||
setMinInterval(minIntervalVal)
|
setMinInterval(minIntervalVal)
|
||||||
|
setCheckInterval(checkIntervalVal)
|
||||||
setPollingEnabled(pollingEnabledVal)
|
setPollingEnabled(pollingEnabledVal)
|
||||||
setPollingInterval(interval)
|
setPollingInterval(interval)
|
||||||
savedPollingIntervalRef.current = interval
|
savedPollingIntervalRef.current = interval
|
||||||
setCountdown(interval)
|
setCountdown(interval)
|
||||||
|
|
||||||
// 返回加载的配置用于后续刷新
|
// 返回加载的配置用于后续刷新
|
||||||
return { target, autoAdd: autoAddVal, minInterval: minIntervalVal, pollingEnabled: pollingEnabledVal, pollingInterval: interval }
|
return { target, autoAdd: autoAddVal, minInterval: minIntervalVal, checkInterval: checkIntervalVal, pollingEnabled: pollingEnabledVal, pollingInterval: interval }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -515,6 +521,16 @@ export default function Monitor() {
|
|||||||
hint="两次自动补号的最小间隔"
|
hint="两次自动补号的最小间隔"
|
||||||
disabled={!autoAdd}
|
disabled={!autoAdd}
|
||||||
/>
|
/>
|
||||||
|
<Input
|
||||||
|
label="检查间隔 (秒)"
|
||||||
|
type="number"
|
||||||
|
min={10}
|
||||||
|
max={300}
|
||||||
|
value={checkInterval}
|
||||||
|
onChange={(e) => setCheckInterval(Number(e.target.value))}
|
||||||
|
hint="自动补号检查频率 (10-300秒)"
|
||||||
|
disabled={!autoAdd}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button onClick={handleSetTarget} loading={loading} className="w-full">
|
<Button onClick={handleSetTarget} loading={loading} className="w-full">
|
||||||
|
|||||||
Reference in New Issue
Block a user