feat(core): 添加断连检测和命令执行功能

- 新增 ConnError 类型用于区分连接级错误和应用层错误
- 在 sync_task 中将网络请求错误包装为 ConnError
- 添加 DisconnectActionConfig 配置结构支持断连操作
- 在配置文件中增加 disconnectAction 配置项
- 实现 executeDisconnectCommand 函数支持跨平台命令执行
- 在主循环中添加断连检测逻辑和阈值判断
- 支持服务器连接恢复时重置断连状态
- 添加详细的日志记录用于断连状态追踪
This commit is contained in:
2026-02-09 23:33:55 +08:00
parent 0b42dad969
commit 5722dd8e5a
4 changed files with 91 additions and 10 deletions

68
main.go
View File

@@ -8,9 +8,12 @@ import (
"ZhenTuLocalPassiveAdapter/logger"
"ZhenTuLocalPassiveAdapter/telemetry"
"context"
"errors"
"fmt"
"os"
"os/exec"
"os/signal"
"runtime"
"syscall"
"time"
@@ -70,10 +73,35 @@ func startTask(device config.DeviceMapping, task dto.Task) {
zap.String("deviceNo", task.DeviceNo))
}
func executeDisconnectCommand(command string) {
go func() {
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.Command("cmd", "/C", command)
} else {
cmd = exec.Command("sh", "-c", command)
}
output, err := cmd.CombinedOutput()
if err != nil {
logger.Error("断连命令执行失败",
zap.String("command", command),
zap.String("output", string(output)),
zap.Error(err))
return
}
logger.Info("断连命令执行成功",
zap.String("command", command),
zap.String("output", string(output)))
}()
}
func runTaskLoop(ctx context.Context) {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
var firstFailureTime time.Time
commandExecuted := false
for {
select {
case <-ctx.Done():
@@ -81,7 +109,43 @@ func runTaskLoop(ctx context.Context) {
case <-ticker.C:
// 执行任务
tasks, err := api.SyncTask()
if err == nil {
if err != nil {
var connErr *api.ConnError
if errors.As(err, &connErr) {
if config.Config.DisconnectAction.Enabled {
// 连接级错误:开始/继续计时
if firstFailureTime.IsZero() {
firstFailureTime = time.Now()
logger.Warn("服务器连接失败,开始计时",
zap.Error(err))
}
threshold := time.Duration(config.Config.DisconnectAction.ThresholdMinutes) * time.Minute
if !commandExecuted && time.Since(firstFailureTime) >= threshold && config.Config.DisconnectAction.Command != "" {
logger.Warn("服务器持续不可达,执行断连命令",
zap.Duration("duration", time.Since(firstFailureTime)),
zap.String("command", config.Config.DisconnectAction.Command))
executeDisconnectCommand(config.Config.DisconnectAction.Command)
commandExecuted = true
}
}
} else {
// 非连接错误(服务器可达,但业务/解析出错):重置断连状态
if !firstFailureTime.IsZero() {
logger.Info("服务器连接已恢复",
zap.Duration("disconnectDuration", time.Since(firstFailureTime)))
firstFailureTime = time.Time{}
commandExecuted = false
}
logger.Error("同步任务失败", zap.Error(err))
}
} else {
// 服务器可达:重置状态
if !firstFailureTime.IsZero() {
logger.Info("服务器连接已恢复",
zap.Duration("disconnectDuration", time.Since(firstFailureTime)))
firstFailureTime = time.Time{}
commandExecuted = false
}
for _, task := range tasks {
logger.Info("开始处理任务",
zap.String("taskID", task.TaskID),
@@ -97,8 +161,6 @@ func runTaskLoop(ctx context.Context) {
}
}
}
} else {
logger.Error("同步任务失败", zap.Error(err))
}
}
}