优化平台运营概览并对齐管理端布局
统一管理端侧栏与顶栏 56px 高度;运营概览按 P2-08 重做 KPI、增长趋势、套餐分布与操作日志。
This commit is contained in:
@@ -17,32 +17,50 @@ type AdminHandler struct{}
|
||||
func NewAdminHandler() *AdminHandler { return &AdminHandler{} }
|
||||
|
||||
func (h *AdminHandler) Stats(c *gin.Context) {
|
||||
var tenantTotal, activeTotal, suspendedTotal, expiringTotal int64
|
||||
var tenantTotal, activeTotal, suspendedTotal, expiringTotal, newThisMonth int64
|
||||
|
||||
model.DB.Model(&model.Tenant{}).Count(&tenantTotal)
|
||||
model.DB.Model(&model.Tenant{}).Where("status = ?", "normal").Count(&activeTotal)
|
||||
model.DB.Model(&model.Tenant{}).Where("status = ?", "suspended").Count(&suspendedTotal)
|
||||
model.DB.Model(&model.Tenant{}).Where("status = ?", "expiring").Count(&expiringTotal)
|
||||
|
||||
now := time.Now()
|
||||
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
||||
model.DB.Model(&model.Tenant{}).Where("created_at >= ?", monthStart).Count(&newThisMonth)
|
||||
|
||||
// 估算月收入:正常/即将到期租户 × 套餐月费
|
||||
type planCount struct {
|
||||
PlanID uint
|
||||
Count int64
|
||||
}
|
||||
var counts []planCount
|
||||
var incomeCounts []planCount
|
||||
model.DB.Model(&model.Tenant{}).
|
||||
Select("plan_id, count(*) as count").
|
||||
Where("status IN ? AND plan_id IS NOT NULL", []string{"normal", "expiring"}).
|
||||
Group("plan_id").
|
||||
Scan(&counts)
|
||||
Scan(&incomeCounts)
|
||||
var monthlyIncome int64
|
||||
planDist := make([]gin.H, 0)
|
||||
for _, item := range counts {
|
||||
for _, item := range incomeCounts {
|
||||
var plan model.Plan
|
||||
if err := model.DB.First(&plan, item.PlanID).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
monthlyIncome += int64(plan.PriceMonthly) * item.Count
|
||||
}
|
||||
|
||||
// 套餐分布:全部已绑定套餐的租户
|
||||
var distCounts []planCount
|
||||
model.DB.Model(&model.Tenant{}).
|
||||
Select("plan_id, count(*) as count").
|
||||
Where("plan_id IS NOT NULL").
|
||||
Group("plan_id").
|
||||
Scan(&distCounts)
|
||||
planDist := make([]gin.H, 0, len(distCounts))
|
||||
for _, item := range distCounts {
|
||||
var plan model.Plan
|
||||
if err := model.DB.First(&plan, item.PlanID).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
planDist = append(planDist, gin.H{
|
||||
"plan_id": plan.ID,
|
||||
"name": plan.Name,
|
||||
@@ -50,18 +68,36 @@ func (h *AdminHandler) Stats(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// 近 6 个月租户增长
|
||||
growth := make([]gin.H, 0, 6)
|
||||
for offset := 5; offset >= 0; offset-- {
|
||||
mStart := time.Date(now.Year(), now.Month()-time.Month(offset), 1, 0, 0, 0, 0, now.Location())
|
||||
mEnd := mStart.AddDate(0, 1, 0)
|
||||
var newCnt, totalCnt int64
|
||||
model.DB.Model(&model.Tenant{}).Where("created_at >= ? AND created_at < ?", mStart, mEnd).Count(&newCnt)
|
||||
model.DB.Model(&model.Tenant{}).Where("created_at < ?", mEnd).Count(&totalCnt)
|
||||
growth = append(growth, gin.H{
|
||||
"month": mStart.Format("1月"),
|
||||
"month_key": mStart.Format("2006-01"),
|
||||
"new_count": newCnt,
|
||||
"total_count": totalCnt,
|
||||
})
|
||||
}
|
||||
|
||||
var recentLogs []model.OperationLog
|
||||
model.DB.Order("created_at desc").Limit(10).Find(&recentLogs)
|
||||
model.DB.Order("created_at desc").Limit(12).Find(&recentLogs)
|
||||
|
||||
middleware.JSON(c, gin.H{
|
||||
"tenant_total": tenantTotal,
|
||||
"active_tenant": activeTotal,
|
||||
"suspended_tenant": suspendedTotal,
|
||||
"expiring_tenant": expiringTotal,
|
||||
"monthly_income": monthlyIncome,
|
||||
"system_uptime": "99.95%",
|
||||
"plan_distribution": planDist,
|
||||
"recent_logs": recentLogs,
|
||||
"tenant_total": tenantTotal,
|
||||
"active_tenant": activeTotal,
|
||||
"suspended_tenant": suspendedTotal,
|
||||
"expiring_tenant": expiringTotal,
|
||||
"new_tenants_month": newThisMonth,
|
||||
"monthly_income": monthlyIncome,
|
||||
"system_uptime": "99.95%",
|
||||
"plan_distribution": planDist,
|
||||
"tenant_growth": growth,
|
||||
"recent_logs": recentLogs,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user