Files
verify/backend/internal/router/admin/dashboard.go
T
admin 488b1581e9 perf: 优化dashboard API,41次查询12次
- 合并30次循环查询为1次(onlineTrend)
- 使用 GROUP BY DATE() 一次获取所有日期数据
- 在内存中填充缺失日期,避免 N+1 问题
- API 响应时间预计从 1-2s 降至 100-200ms
2026-05-06 07:55:36 +08:00

178 lines
5.1 KiB
Go

package admin
import (
"time"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
"verification-platform-backend/pkg/response"
"github.com/gin-gonic/gin"
)
func SetupDashboardRoutes(r *gin.RouterGroup) {
r.GET("/dashboard", handleGetDashboard)
}
func handleGetDashboard(c *gin.Context) {
userID := c.GetUint("user_id")
var stats struct {
TotalApplications int64 `json:"totalApplications"`
TotalUsers int64 `json:"totalUsers"`
TotalCards int64 `json:"totalCards"`
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"`
ExpireDate string `json:"expireDate"`
APIQuota int `json:"apiQuota"`
APIUsed int `json:"apiUsed"`
AppCount int `json:"appCount"`
CanCreateAgent bool `json:"canCreateAgent"`
StorageQuota int64 `json:"storageQuota"`
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 tickets []model.Ticket
database.DB.Where("user_id = ?", userID).Order("created_at DESC").Limit(5).Find(&tickets)
for _, ticket := range tickets {
recentTickets = append(recentTickets, gin.H{
"id": ticket.ID,
"title": ticket.Title,
"status": ticket.Status,
"priority": ticket.Priority,
"created_at": ticket.CreatedAt,
})
}
response.Success(c, gin.H{
"stats": stats,
"subscription": subscription,
"userDistribution": userDistribution,
"onlineTrend": onlineTrend,
"recentTickets": recentTickets,
})
}