package scheduler import ( "context" "fmt" "sync" "time" "git.zkcoi.com/zkcoi/meshray/internal/model" "git.zkcoi.com/zkcoi/meshray/internal/service" "go.uber.org/zap" "gorm.io/gorm" ) // DDNSUpdaterService DDNS 自动更新调度服务 type DDNSUpdaterService struct { db *gorm.DB logger *zap.Logger ipDetection *service.IPDetectionService ddnsOperation *service.DDNSOperationService ctx context.Context cancel context.CancelFunc wg sync.WaitGroup checkInterval time.Duration // 检测间隔 updateThreshold int // IP 变化阈值(连续多少次不同才更新) } // NewDDNSUpdaterService 创建 DDNS 自动更新服务 func NewDDNSUpdaterService( db *gorm.DB, logger *zap.Logger, checkInterval time.Duration, ) *DDNSUpdaterService { ctx, cancel := context.WithCancel(context.Background()) return &DDNSUpdaterService{ db: db, logger: logger, ipDetection: service.NewIPDetectionService(), ddnsOperation: service.NewDDNSOperationService(logger, db), ctx: ctx, cancel: cancel, checkInterval: checkInterval, updateThreshold: 2, // 默认连续 2 次检测到不同 IP 才更新 } } // Start 启动后台自动更新任务 func (s *DDNSUpdaterService) Start() error { s.logger.Info("启动 DDNS 自动更新服务", zap.Duration("检测间隔", s.checkInterval), zap.Int("变化阈值", s.updateThreshold)) s.wg.Add(1) go s.runUpdater() return nil } // Stop 停止后台自动更新任务 func (s *DDNSUpdaterService) Stop() { s.logger.Info("停止 DDNS 自动更新服务") s.cancel() s.wg.Wait() } // runUpdater 运行自动更新协程 func (s *DDNSUpdaterService) runUpdater() { defer s.wg.Done() ticker := time.NewTicker(s.checkInterval) defer ticker.Stop() // 用于跟踪每个 DDNS 服务的连续不同 IP 次数 consecutiveChanges := make(map[string]int) lastIPs := make(map[string]string) for { select { case <-ticker.C: s.checkAndUpdate(consecutiveChanges, lastIPs) case <-s.ctx.Done(): s.logger.Info("DDNS 自动更新服务已停止") return } } } // checkAndUpdate 检测并更新 DDNS 记录 func (s *DDNSUpdaterService) checkAndUpdate( consecutiveChanges map[string]int, lastIPs map[string]string, ) { s.logger.Debug("开始检测 DDNS 服务 IP 变化") // 查询所有启用的 DDNS 全功能模式服务 var ddnsServices []model.Service if err := s.db.Where("type = ? AND config_mode = ? AND enabled = ?", "DDNS", "fullservice", true).Find(&ddnsServices).Error; err != nil { s.logger.Error("查询 DDNS 服务失败", zap.Error(err)) return } s.logger.Info("找到启用的 DDNS 全功能服务", zap.Int("数量", len(ddnsServices))) for _, svc := range ddnsServices { s.wg.Add(1) go func(service model.Service) { defer s.wg.Done() s.processSingleService(&service, consecutiveChanges, lastIPs) }(svc) } } // processSingleService 处理单个 DDNS 服务 func (s *DDNSUpdaterService) processSingleService( service *model.Service, consecutiveChanges map[string]int, lastIPs map[string]string, ) { serviceID := service.ID recordType := service.RecordType // 只处理 A 和 AAAA 记录(需要 IP 检测) if recordType != "A" && recordType != "AAAA" { return } // 确定要比较的字段 var currentConfigIP string switch recordType { case "A": currentConfigIP = service.TargetIP case "AAAA": currentConfigIP = service.TargetIP } if currentConfigIP == "" { s.logger.Warn("DDNS 服务目标 IP 为空,跳过检测", zap.String("service_id", serviceID)) return } // 检测当前公网 IP detectedIP, err := s.ipDetection.DetectIP(recordType) if err != nil { s.logger.Error("检测 IP 失败", zap.String("service_id", serviceID), zap.String("record_type", recordType), zap.Error(err)) return } s.logger.Debug("IP 检测结果", zap.String("service_id", serviceID), zap.String("record_type", recordType), zap.String("配置 IP", currentConfigIP), zap.String("检测 IP", detectedIP)) // 检查 IP 是否变化 mapKey := fmt.Sprintf("%s_%s", serviceID, recordType) if detectedIP != currentConfigIP { // IP 不同,增加计数 consecutiveChanges[mapKey]++ s.logger.Debug("IP 不一致", zap.String("service_id", serviceID), zap.Int("连续次数", consecutiveChanges[mapKey]), zap.Int("阈值", s.updateThreshold)) // 达到阈值才更新 if consecutiveChanges[mapKey] >= s.updateThreshold { s.logger.Info("IP 变化达到阈值,开始更新 DNS 记录", zap.String("service_id", serviceID), zap.String("旧 IP", currentConfigIP), zap.String("新 IP", detectedIP)) // 更新 DNS 记录 err := s.updateDNSRecord(service, detectedIP) if err != nil { s.logger.Error("更新 DNS 记录失败", zap.String("service_id", serviceID), zap.Error(err)) } else { s.logger.Info("DNS 记录更新成功", zap.String("service_id", serviceID), zap.String("新 IP", detectedIP)) // 重置计数 consecutiveChanges[mapKey] = 0 lastIPs[mapKey] = detectedIP } } } else { // IP 相同,重置计数 if consecutiveChanges[mapKey] > 0 { s.logger.Debug("IP 恢复一致,重置计数器", zap.String("service_id", serviceID)) consecutiveChanges[mapKey] = 0 } } // 更新最后检测的 IP lastIPs[mapKey] = detectedIP } // updateDNSRecord 更新 DNS 记录 func (s *DDNSUpdaterService) updateDNSRecord(svc *model.Service, newIP string) error { // 使用 DDNSOperationService 的 UpdateDNSRecord 方法 err := s.ddnsOperation.UpdateDNSRecord(svc, svc.RecordType, svc.Subdomain, newIP, 300) if err != nil { return fmt.Errorf("更新 DNS 记录失败:%w", err) } // 更新数据库中的 IP if err := s.db.Model(&model.Service{}).Where("id = ?", svc.ID).Update("target_ip", newIP).Error; err != nil { s.logger.Warn("更新数据库中的 IP 失败", zap.String("service_id", svc.ID), zap.Error(err)) } return nil }