package handlers import ( "net/http" "strconv" "github.com/gin-gonic/gin" "go_blog/models" "gorm.io/gorm" ) // ViewAnalyticsPage renders the admin analytics page showing article view statistics. func ViewAnalyticsPage(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { tr := getTr(c) // Parse filters from query params articleTitle := c.Query("article_title") userIDStr := c.Query("user_id") ip := c.Query("ip") showBots := c.Query("show_bots") == "1" page := 1 if p := c.Query("page"); p != "" { if parsed, err := strconv.Atoi(p); err == nil && parsed > 0 { page = parsed } } // Build query for view records query := db.Model(&models.ArticleView{}). Preload("Article"). Preload("User"). Order("created_at DESC") // Filter by article title (join with articles table) if articleTitle != "" { query = query.Joins("JOIN articles ON article_views.article_id = articles.id"). Where("articles.title LIKE ?", "%"+articleTitle+"%") } if userIDStr != "" { query = query.Where("user_id = ?", userIDStr) } if ip != "" { query = query.Where("ip LIKE ?", "%"+ip+"%") } if !showBots { query = query.Where("is_bot = ?", false) } // Pagination pageSize := 50 offset := (page - 1) * pageSize var views []models.ArticleView var totalCount int64 query.Count(&totalCount) query.Limit(pageSize).Offset(offset).Find(&views) hasMore := int64(offset+len(views)) < totalCount // Calculate global statistics var stats struct { TotalViews int64 UniqueIPs int64 UniqueUsers int64 BotViews int64 HumanViews int64 } db.Model(&models.ArticleView{}).Count(&stats.TotalViews) db.Model(&models.ArticleView{}).Where("is_bot = ?", true).Count(&stats.BotViews) db.Model(&models.ArticleView{}).Where("is_bot = ?", false).Count(&stats.HumanViews) db.Model(&models.ArticleView{}).Distinct("ip").Count(&stats.UniqueIPs) db.Model(&models.ArticleView{}).Where("user_id IS NOT NULL").Distinct("user_id").Count(&stats.UniqueUsers) // Per-article statistics type ArticleStat struct { ArticleID uint Title string ViewCount int64 UniqueIPs int64 BotViews int64 HumanViews int64 } var articleStats []ArticleStat db.Raw(` SELECT av.article_id, a.title, COUNT(*) as view_count, COUNT(DISTINCT av.ip) as unique_ips, SUM(CASE WHEN av.is_bot THEN 1 ELSE 0 END) as bot_views, SUM(CASE WHEN NOT av.is_bot THEN 1 ELSE 0 END) as human_views FROM article_views av LEFT JOIN articles a ON av.article_id = a.id WHERE a.deleted_at IS NULL GROUP BY av.article_id, a.title ORDER BY view_count DESC LIMIT 20 `).Scan(&articleStats) data := DefaultData(c) data["Title"] = tr["analytics_views_title"] data["Views"] = views data["Stats"] = stats data["ArticleStats"] = articleStats data["Filters"] = gin.H{ "ArticleTitle": articleTitle, "UserID": userIDStr, "IP": ip, "ShowBots": showBots, "Page": page, } data["Pagination"] = gin.H{ "Page": page, "NextPage": page + 1, "HasMore": hasMore, "Total": totalCount, } c.HTML(http.StatusOK, "analytics_views", data) } }