Skip to content

Commit ee362eb

Browse files
author
liuhang
committed
统计网站总访问量
1 parent 1364447 commit ee362eb

File tree

4 files changed

+53
-13
lines changed

4 files changed

+53
-13
lines changed

cmd/server/internal/handler/article_handler.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,38 @@ func (h *ArticleHandler) RecordArticleVisit(c *gin.Context) {
3939
return
4040
}
4141

42-
// 返回结果
4342
c.JSON(http.StatusOK, gin.H{
4443
"path": req.Path,
4544
"views": count,
4645
})
4746
}
4847

48+
func (h *ArticleHandler) RecordSiteVisit(c *gin.Context) {
49+
// 调用 Repo 增加全站计数
50+
err := h.Repo.IncreaseSiteVisit()
51+
if err != nil {
52+
c.JSON(http.StatusOK, gin.H{"status": "ok"})
53+
return
54+
}
55+
c.JSON(http.StatusOK, gin.H{"status": "ok"})
56+
57+
}
58+
4959
// GetSiteInfo 处理 GET /api/site/info
5060
func (h *ArticleHandler) GetSiteInfo(c *gin.Context) {
5161
// 从数据库查统计
52-
totalViews, err := h.Repo.GetSiteStats()
62+
totalViews, siteTotalVisits, err := h.Repo.GetSiteStats()
5363
if err != nil {
5464
c.JSON(http.StatusInternalServerError, gin.H{"error": "数据库查询失败"})
5565
return
5666
}
5767

58-
// 计算运行时间 (当前时间 - 启动时间)
5968
uptime := time.Since(h.StartTime)
6069

6170
c.JSON(http.StatusOK, gin.H{
6271
"total_views": totalViews,
63-
"uptime_seconds": int64(uptime.Seconds()), // 返回秒数,前端处理成 "xx天xx小时" 更灵活
72+
"site_total_visits": siteTotalVisits,
73+
"uptime_seconds": int64(uptime.Seconds()),
6474
"start_time": h.StartTime.Format(time.RFC3339),
6575
})
6676
}

cmd/server/internal/model/article.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ import "time"
44

55
// ArticleStat 对应数据库中的 article_stats 表
66
type ArticleStat struct {
7-
// Path 是文章的 URL 路径,作为主键 (例如 /p/hello-world/)
87
Path string `gorm:"primaryKey;type:varchar(191)"`
98
ViewCount int64 `gorm:"default:0"` // 浏览量
109
CreatedAt time.Time // 创建时间 (自动维护)
1110
UpdatedAt time.Time // 更新时间 (自动维护)
11+
}
12+
13+
type SiteStat struct {
14+
ID uint `gorm:"primaryKey"`
15+
ViewCount int64 `gorm:"default:0"`
1216
}

cmd/server/internal/repository/article_repo.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import (
88

99
type ArticleRepository interface {
1010
IncreaseViewCount(path string) (int64, error)
11-
GetSiteStats() (int64, error)
11+
IncreaseSiteVisit() error
12+
GetSiteStats() (int64, int64, error)
1213
}
1314

1415
type articleRepo struct {
@@ -40,15 +41,39 @@ func (r *articleRepo) IncreaseViewCount(path string) (int64, error) {
4041
return stat.ViewCount, err
4142
}
4243

43-
func (r *articleRepo) GetSiteStats() (int64, error) {
44+
func (r *articleRepo) IncreaseSiteVisit() error {
45+
return r.db.Clauses(clause.OnConflict{
46+
Columns: []clause.Column{{Name: "id"}}, // 冲突列是 ID
47+
DoUpdates: clause.Assignments(map[string]interface{}{
48+
"view_count": gorm.Expr("view_count + 1"),
49+
}),
50+
}).Create(&model.SiteStat{ID: 1, ViewCount: 1}).Error
51+
}
52+
53+
54+
func (r *articleRepo) GetSiteStats() (int64, int64, error) {
4455
var totalViews int64
56+
var siteTotalVisits int64
4557

4658
// 计算总阅读量 (SUM view_count)
4759
// COALESCE 防止没有数据时返回 NULL 导致报错
48-
err := r.db.Model(&model.ArticleStat{}).Select("COALESCE(SUM(view_count), 0)").Scan(&totalViews).Error
60+
err := r.db.Model(&model.ArticleStat{}).
61+
Select("COALESCE(SUM(view_count), 0)").
62+
Scan(&totalViews).Error
63+
4964
if err != nil {
50-
return 0, err
65+
return 0, 0, err
66+
}
67+
68+
// 计算全站访问量 (SELECT view_count FROM site_stats WHERE id = 1)
69+
var stat model.SiteStat
70+
err = r.db.Model(&model.SiteStat{}).
71+
Where("id = ?", 1).
72+
First(&stat).Error
73+
74+
if err == nil {
75+
siteTotalVisits = stat.ViewCount
5176
}
5277

53-
return totalViews, nil
78+
return totalViews, siteTotalVisits, nil
5479
}

cmd/server/main.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
func main() {
1818
dsn := os.Getenv("DSN")
1919
if dsn == "" {
20-
dsn = "root:GB_UXa>cX*h!K2@tcp(127.0.0.1:3306)/blog_db?charset=utf8mb4&parseTime=True&loc=Local"
20+
log.Fatal("DSN 环境变量未设置")
2121
}
2222

2323
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
@@ -26,7 +26,7 @@ func main() {
2626
}
2727
siteFoundingDate := time.Date(2025, 12, 10, 0, 0, 0, 0, time.Local)
2828

29-
db.AutoMigrate(&model.ArticleStat{})
29+
db.AutoMigrate(&model.ArticleStat{}, &model.SiteStat{})
3030

3131
articleRepo := repository.NewArticleRepo(db)
3232
articleHandler := handler.NewArticleHandler(articleRepo, siteFoundingDate)
@@ -35,7 +35,7 @@ func main() {
3535

3636
// 配置 CORS (允许跨域)
3737
r.Use(cors.New(cors.Config{
38-
AllowOrigins: []string{"https://hangops.top", "http://localhost:1313"}, // 允许的域名
38+
AllowOrigins: []string{"https://hangops.top", "http://localhost:1313"},
3939
AllowMethods: []string{"POST", "GET", "OPTIONS"},
4040
AllowHeaders: []string{"Content-Type"},
4141
AllowCredentials: true,
@@ -44,6 +44,7 @@ func main() {
4444
api := r.Group("/api")
4545
{
4646
api.POST("/article/visit", articleHandler.RecordArticleVisit)
47+
api.POST("/site/visit", articleHandler.RecordSiteVisit)
4748
api.GET("/site/info", articleHandler.GetSiteInfo)
4849
}
4950

0 commit comments

Comments
 (0)