diff --git a/dns/server.go b/dns/server.go index 4d981a3..86afde1 100644 --- a/dns/server.go +++ b/dns/server.go @@ -33,6 +33,8 @@ type StatsData struct { BlockedDomains map[string]*BlockedDomain `json:"blockedDomains"` ResolvedDomains map[string]*BlockedDomain `json:"resolvedDomains"` HourlyStats map[string]int64 `json:"hourlyStats"` + DailyStats map[string]int64 `json:"dailyStats"` + MonthlyStats map[string]int64 `json:"monthlyStats"` LastSaved time.Time `json:"lastSaved"` } @@ -53,6 +55,10 @@ type Server struct { resolvedDomains map[string]*BlockedDomain // 用于记录解析的域名 hourlyStatsMutex sync.RWMutex hourlyStats map[string]int64 // 按小时统计屏蔽数量 + dailyStatsMutex sync.RWMutex + dailyStats map[string]int64 // 按天统计屏蔽数量 + monthlyStatsMutex sync.RWMutex + monthlyStats map[string]int64 // 按月统计屏蔽数量 saveTicker *time.Ticker // 用于定时保存数据 saveDone chan struct{} // 用于通知保存协程停止 } @@ -88,6 +94,8 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie blockedDomains: make(map[string]*BlockedDomain), resolvedDomains: make(map[string]*BlockedDomain), hourlyStats: make(map[string]int64), + dailyStats: make(map[string]int64), + monthlyStats: make(map[string]int64), saveDone: make(chan struct{}), } @@ -354,11 +362,26 @@ func (s *Server) updateBlockedDomainStats(domain string) { } } + // 更新统计数据 + now := time.Now() + // 更新小时统计 - hourKey := time.Now().Format("2006-01-02-15") + hourKey := now.Format("2006-01-02-15") s.hourlyStatsMutex.Lock() s.hourlyStats[hourKey]++ s.hourlyStatsMutex.Unlock() + + // 更新每日统计 + dayKey := now.Format("2006-01-02") + s.dailyStatsMutex.Lock() + s.dailyStats[dayKey]++ + s.dailyStatsMutex.Unlock() + + // 更新每月统计 + monthKey := now.Format("2006-01") + s.monthlyStatsMutex.Lock() + s.monthlyStats[monthKey]++ + s.monthlyStatsMutex.Unlock() } // updateResolvedDomainStats 更新解析域名统计 @@ -469,7 +492,7 @@ func (s *Server) GetRecentBlockedDomains(limit int) []BlockedDomain { return domains } -// GetHourlyStats 获取24小时屏蔽统计 +// GetHourlyStats 获取每小时统计数据 func (s *Server) GetHourlyStats() map[string]int64 { s.hourlyStatsMutex.RLock() defer s.hourlyStatsMutex.RUnlock() @@ -482,6 +505,32 @@ func (s *Server) GetHourlyStats() map[string]int64 { return result } +// GetDailyStats 获取每日统计数据 +func (s *Server) GetDailyStats() map[string]int64 { + s.dailyStatsMutex.RLock() + defer s.dailyStatsMutex.RUnlock() + + // 返回副本 + result := make(map[string]int64) + for k, v := range s.dailyStats { + result[k] = v + } + return result +} + +// GetMonthlyStats 获取每月统计数据 +func (s *Server) GetMonthlyStats() map[string]int64 { + s.monthlyStatsMutex.RLock() + defer s.monthlyStatsMutex.RUnlock() + + // 返回副本 + result := make(map[string]int64) + for k, v := range s.monthlyStats { + result[k] = v + } + return result +} + // loadStatsData 从文件加载统计数据 func (s *Server) loadStatsData() { if s.config.StatsFile == "" { @@ -528,6 +577,18 @@ func (s *Server) loadStatsData() { s.hourlyStats = statsData.HourlyStats } s.hourlyStatsMutex.Unlock() + + s.dailyStatsMutex.Lock() + if statsData.DailyStats != nil { + s.dailyStats = statsData.DailyStats + } + s.dailyStatsMutex.Unlock() + + s.monthlyStatsMutex.Lock() + if statsData.MonthlyStats != nil { + s.monthlyStats = statsData.MonthlyStats + } + s.monthlyStatsMutex.Unlock() logger.Info("统计数据加载成功") } @@ -573,6 +634,20 @@ func (s *Server) saveStatsData() { statsData.HourlyStats[k] = v } s.hourlyStatsMutex.RUnlock() + + s.dailyStatsMutex.RLock() + statsData.DailyStats = make(map[string]int64) + for k, v := range s.dailyStats { + statsData.DailyStats[k] = v + } + s.dailyStatsMutex.RUnlock() + + s.monthlyStatsMutex.RLock() + statsData.MonthlyStats = make(map[string]int64) + for k, v := range s.monthlyStats { + statsData.MonthlyStats[k] = v + } + s.monthlyStatsMutex.RUnlock() // 序列化数据 jsonData, err := json.MarshalIndent(statsData, "", " ") diff --git a/http/server.go b/http/server.go index 4ac1a95..8975766 100644 --- a/http/server.go +++ b/http/server.go @@ -51,6 +51,8 @@ func (s *Server) Start() error { mux.HandleFunc("/api/top-resolved", s.handleTopResolvedDomains) mux.HandleFunc("/api/recent-blocked", s.handleRecentBlockedDomains) mux.HandleFunc("/api/hourly-stats", s.handleHourlyStats) + mux.HandleFunc("/api/daily-stats", s.handleDailyStats) + mux.HandleFunc("/api/monthly-stats", s.handleMonthlyStats) } // 静态文件服务(可后续添加前端界面) @@ -191,6 +193,68 @@ func (s *Server) handleHourlyStats(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(result) } +// handleDailyStats 处理每日统计数据请求 +func (s *Server) handleDailyStats(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // 获取每日统计数据 + dailyStats := s.dnsServer.GetDailyStats() + + // 生成过去7天的时间标签 + labels := make([]string, 7) + data := make([]int64, 7) + now := time.Now() + + for i := 6; i >= 0; i-- { + t := now.AddDate(0, 0, -i) + key := t.Format("2006-01-02") + labels[6-i] = t.Format("01-02") + data[6-i] = dailyStats[key] + } + + result := map[string]interface{}{ + "labels": labels, + "data": data, + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(result) +} + +// handleMonthlyStats 处理每月统计数据请求 +func (s *Server) handleMonthlyStats(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // 获取每日统计数据(用于30天视图) + dailyStats := s.dnsServer.GetDailyStats() + + // 生成过去30天的时间标签 + labels := make([]string, 30) + data := make([]int64, 30) + now := time.Now() + + for i := 29; i >= 0; i-- { + t := now.AddDate(0, 0, -i) + key := t.Format("2006-01-02") + labels[29-i] = t.Format("01-02") + data[29-i] = dailyStats[key] + } + + result := map[string]interface{}{ + "labels": labels, + "data": data, + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(result) +} + // handleShield 处理屏蔽规则管理请求 func (s *Server) handleShield(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -201,9 +265,9 @@ func (s *Server) handleShield(w http.ResponseWriter, r *http.Request) { // 获取规则统计信息 stats := s.shieldManager.GetStats() shieldInfo := map[string]interface{}{ - "updateInterval": s.globalConfig.Shield.UpdateInterval, - "blockMethod": s.globalConfig.Shield.BlockMethod, - "blacklistCount": len(s.globalConfig.Shield.Blacklists), + "updateInterval": s.globalConfig.Shield.UpdateInterval, + "blockMethod": s.globalConfig.Shield.BlockMethod, + "blacklistCount": len(s.globalConfig.Shield.Blacklists), "domainRulesCount": stats["domainRules"], "domainExceptionsCount": stats["domainExceptions"], "regexRulesCount": stats["regexRules"], diff --git a/static/index.html b/static/index.html index 434577a..f386686 100644 --- a/static/index.html +++ b/static/index.html @@ -210,6 +210,22 @@