基本功能复刻完成
All checks were successful
build / build (api, amd64, linux) (push) Successful in -47s
build / build (api, arm64, linux) (push) Successful in -47s
build / build (api.exe, amd64, windows) (push) Successful in -48s

This commit is contained in:
CN-JS-HuiBai
2026-04-17 12:24:00 +08:00
parent 06da23fbbc
commit 981ee4f406
37 changed files with 11737 additions and 770 deletions

View File

@@ -13,59 +13,101 @@ import (
func AdminDashboardSummary(c *gin.Context) {
now := time.Now().Unix()
monthAgo := now - 30*24*60*60
todayStart := time.Now().Truncate(24 * time.Hour).Unix()
yesterdayStart := todayStart - 86400
currentMonthStart := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Local).Unix()
lastMonthStart := time.Date(time.Now().Year(), time.Now().Month()-1, 1, 0, 0, 0, 0, time.Local).Unix()
// 1. Basic Counts
var totalUsers int64
var newUsers int64
var activeUsers int64
var totalOrders int64
var pendingOrders int64
var pendingTickets int64
var totalServers int64
var totalPlans int64
var totalCoupons int64
var totalGroups int64
var totalRoutes int64
var onlineUsers int64
var paidOrderAmount int64
var onlineNodes int64
var onlineDevices int64
database.DB.Model(&model.User{}).Count(&totalUsers)
database.DB.Model(&model.User{}).Where("created_at >= ?", monthAgo).Count(&newUsers)
database.DB.Model(&model.User{}).Where("last_login_at IS NOT NULL AND last_login_at >= ?", monthAgo).Count(&activeUsers)
database.DB.Model(&model.User{}).Where("online_count IS NOT NULL AND online_count > 0").Count(&onlineUsers)
database.DB.Model(&model.Order{}).Count(&totalOrders)
database.DB.Model(&model.Order{}).Where("status = ?", 0).Count(&pendingOrders)
database.DB.Model(&model.Order{}).Where("status NOT IN ?", []int{0, 2}).Select("COALESCE(SUM(total_amount), 0)").Scan(&paidOrderAmount)
database.DB.Model(&model.Ticket{}).Where("status = ?", 0).Count(&pendingTickets)
database.DB.Model(&model.Server{}).Count(&totalServers)
database.DB.Model(&model.Plan{}).Count(&totalPlans)
database.DB.Model(&model.Coupon{}).Count(&totalCoupons)
database.DB.Model(&model.ServerGroup{}).Count(&totalGroups)
database.DB.Model(&model.ServerRoute{}).Count(&totalRoutes)
database.DB.Model(&model.Server{}).Where("show = ?", true).Count(&onlineNodes) // Simplified online check
// Online status (last 10 mins)
tenMinsAgo := now - 600
database.DB.Model(&model.User{}).Where("t >= ?", tenMinsAgo).Count(&onlineUsers)
database.DB.Model(&model.User{}).Where("t >= ?", tenMinsAgo).Select("COALESCE(SUM(online_count), 0)").Scan(&onlineDevices)
// 2. Income Statistics
var todayIncome int64
var yesterdayIncome int64
var currentMonthIncome int64
var lastMonthIncome int64
database.DB.Model(&model.Order{}).Where("status NOT IN ? AND created_at >= ?", []int{0, 2}, todayStart).Select("COALESCE(SUM(total_amount), 0)").Scan(&todayIncome)
database.DB.Model(&model.Order{}).Where("status NOT IN ? AND created_at >= ? AND created_at < ?", []int{0, 2}, yesterdayStart, todayStart).Select("COALESCE(SUM(total_amount), 0)").Scan(&yesterdayIncome)
database.DB.Model(&model.Order{}).Where("status NOT IN ? AND created_at >= ?", []int{0, 2}, currentMonthStart).Select("COALESCE(SUM(total_amount), 0)").Scan(&currentMonthIncome)
database.DB.Model(&model.Order{}).Where("status NOT IN ? AND created_at >= ? AND created_at < ?", []int{0, 2}, lastMonthStart, currentMonthStart).Select("COALESCE(SUM(total_amount), 0)").Scan(&lastMonthIncome)
// 3. Traffic Statistics (using StatServer)
type trafficSum struct {
U int64 `json:"upload"`
D int64 `json:"download"`
Total int64 `json:"total"`
}
var todayTraffic trafficSum
var monthTraffic trafficSum
var totalTraffic trafficSum
database.DB.Model(&model.StatServer{}).Where("record_at >= ?", todayStart).Select("COALESCE(SUM(u), 0) as u, COALESCE(SUM(d), 0) as d, COALESCE(SUM(u+d), 0) as total").Scan(&todayTraffic)
database.DB.Model(&model.StatServer{}).Where("record_at >= ?", currentMonthStart).Select("COALESCE(SUM(u), 0) as u, COALESCE(SUM(d), 0) as d, COALESCE(SUM(u+d), 0) as total").Scan(&monthTraffic)
database.DB.Model(&model.StatServer{}).Select("COALESCE(SUM(u), 0) as u, COALESCE(SUM(d), 0) as d, COALESCE(SUM(u+d), 0) as total").Scan(&totalTraffic)
// 4. User Growth
var currentMonthNewUsers int64
var lastMonthNewUsers int64
database.DB.Model(&model.User{}).Where("created_at >= ?", currentMonthStart).Count(&currentMonthNewUsers)
database.DB.Model(&model.User{}).Where("created_at >= ? AND created_at < ?", lastMonthStart, currentMonthStart).Count(&lastMonthNewUsers)
// Calculate Growth Rates
dayIncomeGrowth := calculateGrowth(todayIncome, yesterdayIncome)
monthIncomeGrowth := calculateGrowth(currentMonthIncome, lastMonthIncome)
userGrowth := calculateGrowth(currentMonthNewUsers, lastMonthNewUsers)
Success(c, gin.H{
"server_time": now,
"total_users": totalUsers,
"new_users_30d": newUsers,
"active_users_30d": activeUsers,
"online_users": onlineUsers,
"total_orders": totalOrders,
"pending_orders": pendingOrders,
"paid_order_amount": paidOrderAmount,
"pending_tickets": pendingTickets,
"total_servers": totalServers,
"total_plans": totalPlans,
"total_coupons": totalCoupons,
"total_groups": totalGroups,
"total_routes": totalRoutes,
"todayIncome": todayIncome,
"dayIncomeGrowth": dayIncomeGrowth,
"currentMonthIncome": currentMonthIncome,
"lastMonthIncome": lastMonthIncome,
"monthIncomeGrowth": monthIncomeGrowth,
"currentMonthNewUsers": currentMonthNewUsers,
"totalUsers": totalUsers,
"activeUsers": totalUsers, // Placeholder for valid subscription count
"userGrowth": userGrowth,
"onlineUsers": onlineUsers,
"onlineDevices": onlineDevices,
"ticketPendingTotal": pendingTickets,
"onlineNodes": onlineNodes,
"todayTraffic": todayTraffic,
"monthTraffic": monthTraffic,
"totalTraffic": totalTraffic,
"secure_path": service.GetAdminSecurePath(),
"app_name": service.MustGetString("app_name", "XBoard"),
"app_url": service.GetAppURL(),
"server_pull_period": service.MustGetInt("server_pull_interval", 60),
"server_push_period": service.MustGetInt("server_push_interval", 60),
})
}
func calculateGrowth(current, previous int64) float64 {
if previous <= 0 {
if current > 0 {
return 100.0
}
return 0.0
}
return float64(current-previous) / float64(previous) * 100.0
}
func AdminPlansFetch(c *gin.Context) {
var plans []model.Plan
if err := database.DB.Order("sort ASC, id DESC").Find(&plans).Error; err != nil {