package handler import ( "encoding/json" "net/http" "sync" "soul-api/internal/database" "soul-api/internal/model" "github.com/gin-gonic/gin" "gorm.io/gorm" ) var paidStatuses = []string{"paid", "completed", "success"} // AdminDashboardStats GET /api/admin/dashboard/stats // 轻量聚合:总用户、付费订单数、付费用户数、总营收、转化率(无订单/用户明细) func AdminDashboardStats(c *gin.Context) { db := database.DB() var ( totalUsers int64 paidOrderCount int64 totalRevenue float64 paidUserCount int64 ) var wg sync.WaitGroup wg.Add(4) go func() { defer wg.Done(); db.Model(&model.User{}).Count(&totalUsers) }() go func() { defer wg.Done(); db.Model(&model.Order{}).Where("status IN ?", paidStatuses).Count(&paidOrderCount) }() go func() { defer wg.Done() db.Model(&model.Order{}).Where("status IN ?", paidStatuses). Select("COALESCE(SUM(amount), 0)").Scan(&totalRevenue) }() go func() { defer wg.Done() db.Table("orders").Where("status IN ?", paidStatuses). Select("COUNT(DISTINCT user_id)").Scan(&paidUserCount) }() wg.Wait() conversionRate := 0.0 if totalUsers > 0 && paidUserCount > 0 { conversionRate = float64(paidUserCount) / float64(totalUsers) * 100 } c.JSON(http.StatusOK, gin.H{ "success": true, "totalUsers": totalUsers, "paidOrderCount": paidOrderCount, "paidUserCount": paidUserCount, "totalRevenue": totalRevenue, "conversionRate": conversionRate, }) } // AdminDashboardRecentOrders GET /api/admin/dashboard/recent-orders func AdminDashboardRecentOrders(c *gin.Context) { db := database.DB() var recentOrders []model.Order db.Where("status IN ?", paidStatuses).Order("created_at DESC").Limit(5).Find(&recentOrders) c.JSON(http.StatusOK, gin.H{"success": true, "recentOrders": buildRecentOrdersOut(db, recentOrders)}) } // AdminDashboardNewUsers GET /api/admin/dashboard/new-users func AdminDashboardNewUsers(c *gin.Context) { db := database.DB() var newUsers []model.User db.Model(&model.User{}).Order("created_at DESC").Limit(10).Find(&newUsers) c.JSON(http.StatusOK, gin.H{"success": true, "newUsers": buildNewUsersOut(newUsers)}) } // AdminDashboardOverview GET /api/admin/dashboard/overview // 数据概览:总用户、付费订单数、付费用户数、总营收、转化率、最近订单、新用户 // 优化:6 组查询并行执行,减少总耗时 func AdminDashboardOverview(c *gin.Context) { db := database.DB() var ( totalUsers int64 paidOrderCount int64 totalRevenue float64 paidUserCount int64 recentOrders []model.Order newUsers []model.User ) var wg sync.WaitGroup wg.Add(6) go func() { defer wg.Done() db.Model(&model.User{}).Count(&totalUsers) }() go func() { defer wg.Done() db.Model(&model.Order{}).Where("status IN ?", paidStatuses).Count(&paidOrderCount) }() go func() { defer wg.Done() db.Model(&model.Order{}).Where("status IN ?", paidStatuses). Select("COALESCE(SUM(amount), 0)").Scan(&totalRevenue) }() go func() { defer wg.Done() db.Table("orders").Where("status IN ?", paidStatuses). Select("COUNT(DISTINCT user_id)").Scan(&paidUserCount) }() go func() { defer wg.Done() db.Where("status IN ?", paidStatuses). Order("created_at DESC").Limit(5).Find(&recentOrders) }() go func() { defer wg.Done() db.Model(&model.User{}).Order("created_at DESC").Limit(10).Find(&newUsers) }() wg.Wait() conversionRate := 0.0 if totalUsers > 0 && paidUserCount > 0 { conversionRate = float64(paidUserCount) / float64(totalUsers) * 100 } recentOut := buildRecentOrdersOut(db, recentOrders) newOut := buildNewUsersOut(newUsers) c.JSON(http.StatusOK, gin.H{ "success": true, "totalUsers": totalUsers, "paidOrderCount": paidOrderCount, "paidUserCount": paidUserCount, "totalRevenue": totalRevenue, "conversionRate": conversionRate, "recentOrders": recentOut, "newUsers": newOut, }) } func dashStr(s *string) string { if s == nil || *s == "" { return "" } return *s } func buildRecentOrdersOut(db *gorm.DB, recentOrders []model.Order) []gin.H { if len(recentOrders) == 0 { return nil } userIDs := make(map[string]bool) for _, o := range recentOrders { if o.UserID != "" { userIDs[o.UserID] = true } } ids := make([]string, 0, len(userIDs)) for id := range userIDs { ids = append(ids, id) } var users []model.User db.Where("id IN ?", ids).Find(&users) userMap := make(map[string]*model.User) for i := range users { userMap[users[i].ID] = &users[i] } out := make([]gin.H, 0, len(recentOrders)) for _, o := range recentOrders { b, _ := json.Marshal(o) var m map[string]interface{} _ = json.Unmarshal(b, &m) if u := userMap[o.UserID]; u != nil { m["userNickname"] = dashStr(u.Nickname) m["userAvatar"] = dashStr(u.Avatar) } else { m["userNickname"] = "" m["userAvatar"] = "" } out = append(out, m) } return out } func buildNewUsersOut(newUsers []model.User) []gin.H { out := make([]gin.H, 0, len(newUsers)) for _, u := range newUsers { out = append(out, gin.H{ "id": u.ID, "nickname": dashStr(u.Nickname), "phone": dashStr(u.Phone), "referralCode": dashStr(u.ReferralCode), "createdAt": u.CreatedAt, }) } return out }