Some checks failed
CI / Build & Test (push) Failing after 1m31s
- Add user management endpoints (list/create/get/update/delete) with pagination and search - Add dashboard stats endpoint with tenant/user/online counts and growth metrics - Add tenant resolver middleware for multi-tenant request scoping - Add i18n kernel with zh/en message files and AcceptLanguage middleware - Add WebSocket hub/handler for real-time communication - Add job scheduler kernel with cron support - Add plugin sandbox for isolated execution - Add storage kernel (local filesystem) - Add event bus kernel for pub/sub - Add cache kernel abstraction - Add database migration runner and version upgrade checker - Add rate limiting middleware with Redis backend - Add SQL migrations for rbac, audit_logs, settings, notifications, examples - Extend user repository with list/delete/count operations - Register all module routes with tenant resolver
98 lines
3.0 KiB
Go
98 lines
3.0 KiB
Go
package interfaces
|
|
|
|
import (
|
|
"time"
|
|
|
|
"mengstack/internal/kernel/response"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type Handler struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewHandler(db *gorm.DB) *Handler {
|
|
return &Handler{db: db}
|
|
}
|
|
|
|
type StatsResponse struct {
|
|
TenantCount int64 `json:"tenantCount"`
|
|
UserCount int64 `json:"userCount"`
|
|
RoleCount int64 `json:"roleCount"`
|
|
OnlineCount int64 `json:"onlineCount"`
|
|
TenantGrowth float64 `json:"tenantGrowth"`
|
|
UserGrowth float64 `json:"userGrowth"`
|
|
RoleGrowth float64 `json:"roleGrowth"`
|
|
TodayActiveUsers int64 `json:"todayActiveUsers"`
|
|
}
|
|
|
|
// GetStats godoc
|
|
// @Summary 仪表盘统计
|
|
// @Description 返回平台核心指标的统计数据
|
|
// @Tags Dashboard
|
|
// @Produce json
|
|
// @Success 200 {object} response.Response{data=StatsResponse}
|
|
// @Security Bearer
|
|
// @Security TenantID
|
|
// @Router /dashboard/stats [get]
|
|
func (h *Handler) GetStats(c *gin.Context) {
|
|
tenantID, _ := c.Get("tenant_id")
|
|
isSuperAdmin := tenantID == ""
|
|
|
|
var tenantCount, userCount, roleCount int64
|
|
var tenantPrev, userPrev, rolePrev int64
|
|
|
|
now := time.Now()
|
|
thirtyDaysAgo := now.AddDate(0, 0, -30)
|
|
sixtyDaysAgo := now.AddDate(0, 0, -60)
|
|
|
|
if isSuperAdmin {
|
|
h.db.Table("tenants").Count(&tenantCount)
|
|
h.db.Table("users").Count(&userCount)
|
|
h.db.Table("roles").Count(&roleCount)
|
|
|
|
h.db.Table("tenants").Where("created_at < ? AND created_at >= ?", thirtyDaysAgo, sixtyDaysAgo).Count(&tenantPrev)
|
|
h.db.Table("users").Where("created_at < ? AND created_at >= ?", thirtyDaysAgo, sixtyDaysAgo).Count(&userPrev)
|
|
h.db.Table("roles").Where("created_at < ? AND created_at >= ?", thirtyDaysAgo, sixtyDaysAgo).Count(&rolePrev)
|
|
} else {
|
|
h.db.Table("users").Where("tenant_id = ?", tenantID).Count(&userCount)
|
|
h.db.Table("roles").Where("tenant_id = ?", tenantID).Count(&roleCount)
|
|
|
|
h.db.Table("users").Where("tenant_id = ? AND created_at < ? AND created_at >= ?", tenantID, thirtyDaysAgo, sixtyDaysAgo).Count(&userPrev)
|
|
h.db.Table("roles").Where("tenant_id = ? AND created_at < ? AND created_at >= ?", tenantID, thirtyDaysAgo, sixtyDaysAgo).Count(&rolePrev)
|
|
}
|
|
|
|
var todayActiveUsers int64
|
|
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
|
if isSuperAdmin {
|
|
h.db.Table("users").Where("last_login >= ?", todayStart).Count(&todayActiveUsers)
|
|
} else {
|
|
h.db.Table("users").Where("tenant_id = ? AND last_login >= ?", tenantID, todayStart).Count(&todayActiveUsers)
|
|
}
|
|
|
|
stats := StatsResponse{
|
|
TenantCount: tenantCount,
|
|
UserCount: userCount,
|
|
RoleCount: roleCount,
|
|
OnlineCount: 0,
|
|
TenantGrowth: calcGrowth(tenantCount, tenantPrev),
|
|
UserGrowth: calcGrowth(userCount, userPrev),
|
|
RoleGrowth: calcGrowth(roleCount, rolePrev),
|
|
TodayActiveUsers: todayActiveUsers,
|
|
}
|
|
|
|
response.Success(c, stats)
|
|
}
|
|
|
|
func calcGrowth(current, previous int64) float64 {
|
|
if previous == 0 {
|
|
if current > 0 {
|
|
return 100
|
|
}
|
|
return 0
|
|
}
|
|
return float64(current-previous) / float64(previous) * 100
|
|
}
|