feat: 管理首页新增会话存储后端状态展示
- 仪表盘新增「会话存储后端」卡片,显示 Redis/内存 模式 - 降级时显示「已降级」标签 + 降级说明 - 内存模式下显示活跃会话数 - 新增 stat-red / stat-desc / stat-badge / badge-warn CSS 样式
This commit is contained in:
@ -29,9 +29,15 @@ func (ac *AdminController) Dashboard(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
totalUsers = 0
|
totalUsers = 0
|
||||||
}
|
}
|
||||||
|
storeMetrics := ac.adminService.GetStoreMetrics()
|
||||||
c.HTML(http.StatusOK, "admin/dashboard/index.html", common.BuildAdminPageData(c, gin.H{
|
c.HTML(http.StatusOK, "admin/dashboard/index.html", common.BuildAdminPageData(c, gin.H{
|
||||||
"Title": "管理首页",
|
"Title": "管理首页",
|
||||||
"TotalUsers": totalUsers,
|
"TotalUsers": totalUsers,
|
||||||
|
"StoreType": storeMetrics.Type,
|
||||||
|
"StoreHealthy": storeMetrics.Healthy,
|
||||||
|
"StoreDegraded": storeMetrics.Degraded,
|
||||||
|
"DegradedNote": storeMetrics.DegradedNote,
|
||||||
|
"ActiveSessions": storeMetrics.ActiveCount,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,14 +3,16 @@ package admin
|
|||||||
import (
|
import (
|
||||||
"metazone.cc/metalab/internal/model"
|
"metazone.cc/metalab/internal/model"
|
||||||
"metazone.cc/metalab/internal/service"
|
"metazone.cc/metalab/internal/service"
|
||||||
|
"metazone.cc/metalab/internal/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
// adminUseCase AdminController 对 AdminService 的最小依赖(ISP:4 个方法)
|
// adminUseCase AdminController 对 AdminService 的最小依赖(ISP:5 个方法)
|
||||||
type adminUseCase interface {
|
type adminUseCase interface {
|
||||||
ListUsers(params service.ListUsersParams) (*service.ListUsersResult, error)
|
ListUsers(params service.ListUsersParams) (*service.ListUsersResult, error)
|
||||||
UpdateUserStatus(operatorUID, targetUID uint, newStatus string) error
|
UpdateUserStatus(operatorUID, targetUID uint, newStatus string) error
|
||||||
ResetUserToken(operatorUID, targetUID uint) error
|
ResetUserToken(operatorUID, targetUID uint) error
|
||||||
CountUsers() (int64, error)
|
CountUsers() (int64, error)
|
||||||
|
GetStoreMetrics() session.StoreMetrics
|
||||||
}
|
}
|
||||||
|
|
||||||
// auditUseCase AuditController 对 AuditService 的最小依赖(ISP:3 个方法)
|
// auditUseCase AuditController 对 AuditService 的最小依赖(ISP:3 个方法)
|
||||||
|
|||||||
@ -112,6 +112,11 @@ func (s *AdminService) CountUsers() (int64, error) {
|
|||||||
return s.userRepo.CountSearchUsers("", "", "")
|
return s.userRepo.CountSearchUsers("", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStoreMetrics 返回会话存储状态信息(管理首页用)
|
||||||
|
func (s *AdminService) GetStoreMetrics() session.StoreMetrics {
|
||||||
|
return s.sessionManager.GetStoreMetrics()
|
||||||
|
}
|
||||||
|
|
||||||
// checkAndOperate 通用权限检查 + 执行操作
|
// checkAndOperate 通用权限检查 + 执行操作
|
||||||
func (s *AdminService) checkAndOperate(operatorUID, targetUID uint, operate func(*model.User, *model.User) error) error {
|
func (s *AdminService) checkAndOperate(operatorUID, targetUID uint, operate func(*model.User, *model.User) error) error {
|
||||||
// 1. 不可操作自己
|
// 1. 不可操作自己
|
||||||
|
|||||||
@ -19,6 +19,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-grid" style="margin-top: 1rem;">
|
||||||
|
<div class="stat-card" style="grid-column: span 2;">
|
||||||
|
<div class="stat-icon {{if .StoreDegraded}}stat-red{{else if eq .StoreType "redis"}}stat-green{{else}}stat-blue{{end}}">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="stat-label">会话存储后端</div>
|
||||||
|
<div class="stat-value">
|
||||||
|
{{if eq .StoreType "redis"}}Redis{{else}}内存{{end}}
|
||||||
|
{{if .StoreDegraded}}<span class="stat-badge badge-warn">已降级</span>{{end}}
|
||||||
|
</div>
|
||||||
|
{{if .DegradedNote}}<div class="stat-desc">{{.DegradedNote}}</div>{{end}}
|
||||||
|
{{if ge .ActiveSessions 0}}<div class="stat-desc">活跃会话数:{{.ActiveSessions}}</div>{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="info-card">
|
<div class="info-card">
|
||||||
<h3>快速开始</h3>
|
<h3>快速开始</h3>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@ -138,8 +138,12 @@ a:hover { color: var(--primary-hover); }
|
|||||||
.stat-blue { background: #e3f2fd; color: var(--primary); }
|
.stat-blue { background: #e3f2fd; color: var(--primary); }
|
||||||
.stat-green { background: #e8f5e9; color: var(--success); }
|
.stat-green { background: #e8f5e9; color: var(--success); }
|
||||||
.stat-orange { background: #fff3e0; color: var(--warning); }
|
.stat-orange { background: #fff3e0; color: var(--warning); }
|
||||||
|
.stat-red { background: #ffebee; color: var(--danger, #e53935); }
|
||||||
.stat-label { font-size: 12px; color: var(--text-muted); }
|
.stat-label { font-size: 12px; color: var(--text-muted); }
|
||||||
.stat-value { font-size: 22px; font-weight: 600; color: var(--text); }
|
.stat-value { font-size: 22px; font-weight: 600; color: var(--text); }
|
||||||
|
.stat-desc { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
|
||||||
|
.stat-badge { display: inline-block; font-size: 11px; padding: 1px 8px; border-radius: 10px; margin-left: 8px; vertical-align: middle; }
|
||||||
|
.badge-warn { background: #fff3e0; color: var(--warning); }
|
||||||
|
|
||||||
/* --- 信息卡片 --- */
|
/* --- 信息卡片 --- */
|
||||||
.info-card {
|
.info-card {
|
||||||
|
|||||||
Reference in New Issue
Block a user