mengstack-api/internal/modules/dashboard/interfaces/handler.go
MengStack Dev df809f1045
Some checks failed
CI / Build & Test (push) Failing after 1m31s
feat: user CRUD API + dashboard stats + kernel infrastructure
- 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
2026-10-03 03:42:58 +08:00

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
}