perf: 优化 Dashboard 接口性能,并行执行数据库查询

- 使用 goroutine 并行执行 8 个数据库查询
- 将串行查询改为并行,显著减少响应时间
- 使用 sync.WaitGroup 和 sync.Mutex 保证线程安全
This commit is contained in:
2026-05-07 13:58:28 +08:00
parent 8161a3f75f
commit f290b81700
+80 -35
View File
@@ -1,6 +1,7 @@
package admin package admin
import ( import (
"sync"
"time" "time"
"verification-platform-backend/internal/database" "verification-platform-backend/internal/database"
"verification-platform-backend/internal/model" "verification-platform-backend/internal/model"
@@ -23,29 +24,6 @@ func handleGetDashboard(c *gin.Context) {
MonthlyRevenue float64 `json:"monthlyRevenue"` MonthlyRevenue float64 `json:"monthlyRevenue"`
} }
database.DB.Model(&model.Application{}).Where("user_id = ?", userID).Count(&stats.TotalApplications)
var appIDs []uint
database.DB.Model(&model.Application{}).Where("user_id = ?", userID).Pluck("id", &appIDs)
if len(appIDs) > 0 {
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Count(&stats.TotalUsers)
}
database.DB.Model(&model.Card{}).Where("creator_id = ?", userID).Count(&stats.TotalCards)
var monthlyRevenue float64
if len(appIDs) > 0 {
var appUserIDs []uint
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Pluck("id", &appUserIDs)
if len(appUserIDs) > 0 {
database.DB.Model(&model.RechargeRecord{}).
Where("user_id IN ? AND status = ? AND created_at >= ?", appUserIDs, "success", time.Now().AddDate(0, -1, 0)).
Select("COALESCE(SUM(amount), 0)").
Scan(&monthlyRevenue)
}
}
stats.MonthlyRevenue = monthlyRevenue
var subscription struct { var subscription struct {
Plan string `json:"plan"` Plan string `json:"plan"`
Status string `json:"status"` Status string `json:"status"`
@@ -58,6 +36,57 @@ func handleGetDashboard(c *gin.Context) {
StorageUsed int64 `json:"storageUsed"` StorageUsed int64 `json:"storageUsed"`
} }
userDistribution := gin.H{
"provinces": []gin.H{},
"overseas": []gin.H{},
}
onlineTrend := make([]gin.H, 0, 30)
recentTickets := make([]gin.H, 0, 5)
var appIDs []uint
database.DB.Model(&model.Application{}).Where("user_id = ?", userID).Pluck("id", &appIDs)
var wg sync.WaitGroup
var mu sync.Mutex
wg.Add(8)
go func() {
defer wg.Done()
database.DB.Model(&model.Application{}).Where("user_id = ?", userID).Count(&stats.TotalApplications)
}()
go func() {
defer wg.Done()
database.DB.Model(&model.Card{}).Where("creator_id = ?", userID).Count(&stats.TotalCards)
}()
go func() {
defer wg.Done()
if len(appIDs) > 0 {
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Count(&stats.TotalUsers)
}
}()
go func() {
defer wg.Done()
if len(appIDs) > 0 {
var appUserIDs []uint
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Pluck("id", &appUserIDs)
if len(appUserIDs) > 0 {
var revenue float64
database.DB.Model(&model.RechargeRecord{}).
Where("user_id IN ? AND status = ? AND created_at >= ?", appUserIDs, "success", time.Now().AddDate(0, -1, 0)).
Select("COALESCE(SUM(amount), 0)").
Scan(&revenue)
stats.MonthlyRevenue = revenue
}
}
}()
go func() {
defer wg.Done()
var user model.User var user model.User
if err := database.DB.First(&user, userID).Error; err == nil { if err := database.DB.First(&user, userID).Error; err == nil {
subscription.Plan = "基础版" subscription.Plan = "基础版"
@@ -70,10 +99,10 @@ func handleGetDashboard(c *gin.Context) {
} }
subscription.ExpireDate = "永久" subscription.ExpireDate = "永久"
} }
subscription.AppCount = int(stats.TotalApplications) }()
subscription.APIQuota = 10000
subscription.StorageQuota = 100 * 1024 * 1024
go func() {
defer wg.Done()
var apiUsed int64 var apiUsed int64
database.DB.Model(&model.ApiUsage{}).Where("user_id = ?", userID).Count(&apiUsed) database.DB.Model(&model.ApiUsage{}).Where("user_id = ?", userID).Count(&apiUsed)
subscription.APIUsed = int(apiUsed) subscription.APIUsed = int(apiUsed)
@@ -81,12 +110,10 @@ func handleGetDashboard(c *gin.Context) {
var storageUsed int64 var storageUsed int64
database.DB.Model(&model.StorageUsage{}).Where("user_id = ?", userID).Select("COALESCE(SUM(size), 0)").Scan(&storageUsed) database.DB.Model(&model.StorageUsage{}).Where("user_id = ?", userID).Select("COALESCE(SUM(size), 0)").Scan(&storageUsed)
subscription.StorageUsed = storageUsed subscription.StorageUsed = storageUsed
}()
userDistribution := gin.H{ go func() {
"provinces": []gin.H{}, defer wg.Done()
"overseas": []gin.H{},
}
if len(appIDs) > 0 { if len(appIDs) > 0 {
type DeviceCount struct { type DeviceCount struct {
DeviceType string DeviceType string
@@ -107,10 +134,14 @@ func handleGetDashboard(c *gin.Context) {
"count": dc.Count, "count": dc.Count,
}) })
} }
mu.Lock()
userDistribution["provinces"] = provinces userDistribution["provinces"] = provinces
mu.Unlock()
} }
}()
onlineTrend := make([]gin.H, 0, 30) go func() {
defer wg.Done()
if len(appIDs) > 0 { if len(appIDs) > 0 {
type DailyCount struct { type DailyCount struct {
Date string Date string
@@ -134,27 +165,41 @@ func handleGetDashboard(c *gin.Context) {
countMap[dc.Date] = dc.Count countMap[dc.Date] = dc.Count
} }
trend := make([]gin.H, 0, 30)
for i := 29; i >= 0; i-- { for i := 29; i >= 0; i-- {
date := time.Now().AddDate(0, 0, -i) date := time.Now().AddDate(0, 0, -i)
dateStr := date.Format("2006-01-02") dateStr := date.Format("2006-01-02")
count := countMap[dateStr] count := countMap[dateStr]
onlineTrend = append(onlineTrend, gin.H{ trend = append(trend, gin.H{
"date": date.Format("01-02"), "date": date.Format("01-02"),
"value": count, "value": count,
}) })
} }
mu.Lock()
onlineTrend = trend
mu.Unlock()
} else { } else {
trend := make([]gin.H, 0, 30)
for i := 29; i >= 0; i-- { for i := 29; i >= 0; i-- {
date := time.Now().AddDate(0, 0, -i) date := time.Now().AddDate(0, 0, -i)
onlineTrend = append(onlineTrend, gin.H{ trend = append(trend, gin.H{
"date": date.Format("01-02"), "date": date.Format("01-02"),
"value": 0, "value": 0,
}) })
} }
mu.Lock()
onlineTrend = trend
mu.Unlock()
} }
}()
wg.Wait()
subscription.AppCount = int(stats.TotalApplications)
subscription.APIQuota = 10000
subscription.StorageQuota = 100 * 1024 * 1024
recentTickets := make([]gin.H, 0, 5)
var tickets []model.Ticket var tickets []model.Ticket
database.DB.Where("user_id = ?", userID).Order("created_at DESC").Limit(5).Find(&tickets) database.DB.Where("user_id = ?", userID).Order("created_at DESC").Limit(5).Find(&tickets)
for _, ticket := range tickets { for _, ticket := range tickets {