diff --git a/backend/internal/router/admin/dashboard.go b/backend/internal/router/admin/dashboard.go index a30f881..057a151 100644 --- a/backend/internal/router/admin/dashboard.go +++ b/backend/internal/router/admin/dashboard.go @@ -1,6 +1,7 @@ package admin import ( + "sync" "time" "verification-platform-backend/internal/database" "verification-platform-backend/internal/model" @@ -23,29 +24,6 @@ func handleGetDashboard(c *gin.Context) { 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 { Plan string `json:"plan"` Status string `json:"status"` @@ -58,103 +36,170 @@ func handleGetDashboard(c *gin.Context) { StorageUsed int64 `json:"storageUsed"` } - var user model.User - if err := database.DB.First(&user, userID).Error; err == nil { - subscription.Plan = "基础版" - if user.Role == "admin" { - subscription.Plan = "管理员" - } - subscription.Status = "正常" - if user.Status == "banned" { - subscription.Status = "已禁用" - } - subscription.ExpireDate = "永久" - } - subscription.AppCount = int(stats.TotalApplications) - subscription.APIQuota = 10000 - subscription.StorageQuota = 100 * 1024 * 1024 - - var apiUsed int64 - database.DB.Model(&model.ApiUsage{}).Where("user_id = ?", userID).Count(&apiUsed) - subscription.APIUsed = int(apiUsed) - - var storageUsed int64 - database.DB.Model(&model.StorageUsage{}).Where("user_id = ?", userID).Select("COALESCE(SUM(size), 0)").Scan(&storageUsed) - subscription.StorageUsed = storageUsed - userDistribution := gin.H{ "provinces": []gin.H{}, "overseas": []gin.H{}, } - if len(appIDs) > 0 { - type DeviceCount struct { - DeviceType string - Count int - } - var deviceCounts []DeviceCount - database.DB.Model(&model.UserDevice{}). - Select("device_type, COUNT(*) as count"). - Where("application_id IN ?", appIDs). - Group("device_type"). - Order("count DESC"). - Find(&deviceCounts) - - provinces := make([]gin.H, 0, len(deviceCounts)) - for _, dc := range deviceCounts { - provinces = append(provinces, gin.H{ - "name": dc.DeviceType, - "count": dc.Count, - }) - } - userDistribution["provinces"] = provinces - } - onlineTrend := make([]gin.H, 0, 30) - if len(appIDs) > 0 { - type DailyCount struct { - Date string - Count int64 - } - var dailyCounts []DailyCount - - dateStart := time.Now().AddDate(0, 0, -29) - dateStart = time.Date(dateStart.Year(), dateStart.Month(), dateStart.Day(), 0, 0, 0, 0, dateStart.Location()) - - database.DB.Model(&model.DeviceSession{}). - Select("DATE(last_heartbeat) as date, COUNT(*) as count"). - Joins("JOIN user_devices ON device_sessions.device_id = user_devices.id"). - Where("user_devices.application_id IN ? AND last_heartbeat >= ?", appIDs, dateStart). - Group("DATE(last_heartbeat)"). - Order("date ASC"). - Find(&dailyCounts) - - countMap := make(map[string]int64) - for _, dc := range dailyCounts { - countMap[dc.Date] = dc.Count - } - - for i := 29; i >= 0; i-- { - date := time.Now().AddDate(0, 0, -i) - dateStr := date.Format("2006-01-02") - count := countMap[dateStr] - - onlineTrend = append(onlineTrend, gin.H{ - "date": date.Format("01-02"), - "value": count, - }) - } - } else { - for i := 29; i >= 0; i-- { - date := time.Now().AddDate(0, 0, -i) - onlineTrend = append(onlineTrend, gin.H{ - "date": date.Format("01-02"), - "value": 0, - }) - } - } - 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 + if err := database.DB.First(&user, userID).Error; err == nil { + subscription.Plan = "基础版" + if user.Role == "admin" { + subscription.Plan = "管理员" + } + subscription.Status = "正常" + if user.Status == "banned" { + subscription.Status = "已禁用" + } + subscription.ExpireDate = "永久" + } + }() + + go func() { + defer wg.Done() + var apiUsed int64 + database.DB.Model(&model.ApiUsage{}).Where("user_id = ?", userID).Count(&apiUsed) + subscription.APIUsed = int(apiUsed) + + var storageUsed int64 + database.DB.Model(&model.StorageUsage{}).Where("user_id = ?", userID).Select("COALESCE(SUM(size), 0)").Scan(&storageUsed) + subscription.StorageUsed = storageUsed + }() + + go func() { + defer wg.Done() + if len(appIDs) > 0 { + type DeviceCount struct { + DeviceType string + Count int + } + var deviceCounts []DeviceCount + database.DB.Model(&model.UserDevice{}). + Select("device_type, COUNT(*) as count"). + Where("application_id IN ?", appIDs). + Group("device_type"). + Order("count DESC"). + Find(&deviceCounts) + + provinces := make([]gin.H, 0, len(deviceCounts)) + for _, dc := range deviceCounts { + provinces = append(provinces, gin.H{ + "name": dc.DeviceType, + "count": dc.Count, + }) + } + mu.Lock() + userDistribution["provinces"] = provinces + mu.Unlock() + } + }() + + go func() { + defer wg.Done() + if len(appIDs) > 0 { + type DailyCount struct { + Date string + Count int64 + } + var dailyCounts []DailyCount + + dateStart := time.Now().AddDate(0, 0, -29) + dateStart = time.Date(dateStart.Year(), dateStart.Month(), dateStart.Day(), 0, 0, 0, 0, dateStart.Location()) + + database.DB.Model(&model.DeviceSession{}). + Select("DATE(last_heartbeat) as date, COUNT(*) as count"). + Joins("JOIN user_devices ON device_sessions.device_id = user_devices.id"). + Where("user_devices.application_id IN ? AND last_heartbeat >= ?", appIDs, dateStart). + Group("DATE(last_heartbeat)"). + Order("date ASC"). + Find(&dailyCounts) + + countMap := make(map[string]int64) + for _, dc := range dailyCounts { + countMap[dc.Date] = dc.Count + } + + trend := make([]gin.H, 0, 30) + for i := 29; i >= 0; i-- { + date := time.Now().AddDate(0, 0, -i) + dateStr := date.Format("2006-01-02") + count := countMap[dateStr] + + trend = append(trend, gin.H{ + "date": date.Format("01-02"), + "value": count, + }) + } + mu.Lock() + onlineTrend = trend + mu.Unlock() + } else { + trend := make([]gin.H, 0, 30) + for i := 29; i >= 0; i-- { + date := time.Now().AddDate(0, 0, -i) + trend = append(trend, gin.H{ + "date": date.Format("01-02"), + "value": 0, + }) + } + mu.Lock() + onlineTrend = trend + mu.Unlock() + } + }() + + wg.Wait() + + subscription.AppCount = int(stats.TotalApplications) + subscription.APIQuota = 10000 + subscription.StorageQuota = 100 * 1024 * 1024 + var tickets []model.Ticket database.DB.Where("user_id = ?", userID).Order("created_at DESC").Limit(5).Find(&tickets) for _, ticket := range tickets {