Files
0451meishi/backend/internal/handlers/dashboard.go
2026-01-15 11:37:22 +08:00

114 lines
3.1 KiB
Go

package handlers
import (
"context"
"encoding/json"
"net/http"
"time"
"0451meishiditu/backend/internal/models"
"0451meishiditu/backend/internal/resp"
"github.com/gin-gonic/gin"
)
type dashboardOverview struct {
TotalStores int64 `json:"total_stores"`
TotalReviews int64 `json:"total_reviews"`
TotalCategories int64 `json:"total_categories"`
CategoryDist []categoryKV `json:"category_dist"`
StoresLast7Days []dateKV `json:"stores_last7days"`
TopRatedStores []topStoreRow `json:"top_rated_stores"`
}
type categoryKV struct {
CategoryID uint `json:"category_id"`
Name string `json:"name"`
Count int64 `json:"count"`
}
type dateKV struct {
Date string `json:"date"`
Count int64 `json:"count"`
}
type topStoreRow struct {
StoreID uint `json:"store_id"`
Name string `json:"name"`
Avg float64 `json:"avg"`
Count int64 `json:"count"`
}
func (h *Handlers) DashboardOverview(c *gin.Context) {
ctx := c.Request.Context()
cacheKey := "admin:dashboard:overview:v1"
if b, err := h.rdb.Get(ctx, cacheKey).Bytes(); err == nil && len(b) > 0 {
var cached dashboardOverview
if json.Unmarshal(b, &cached) == nil {
resp.OK(c, cached)
return
}
}
ov, err := h.buildDashboard(ctx)
if err != nil {
resp.Fail(c, http.StatusInternalServerError, "db error")
return
}
if b, err := json.Marshal(ov); err == nil {
_ = h.rdb.Set(ctx, cacheKey, b, 30*time.Second).Err()
}
resp.OK(c, ov)
}
func (h *Handlers) buildDashboard(ctx context.Context) (dashboardOverview, error) {
var out dashboardOverview
if err := h.db.WithContext(ctx).Model(&models.Store{}).Count(&out.TotalStores).Error; err != nil {
return out, err
}
if err := h.db.WithContext(ctx).Model(&models.Review{}).Count(&out.TotalReviews).Error; err != nil {
return out, err
}
if err := h.db.WithContext(ctx).Model(&models.Category{}).Count(&out.TotalCategories).Error; err != nil {
return out, err
}
if err := h.db.WithContext(ctx).
Table("stores").
Select("categories.id as category_id, categories.name as name, count(stores.id) as count").
Joins("left join categories on categories.id = stores.category_id").
Where("stores.deleted_at is null").
Group("categories.id, categories.name").
Order("count desc").
Scan(&out.CategoryDist).Error; err != nil {
return out, err
}
if err := h.db.WithContext(ctx).
Table("stores").
Select("date(created_at) as date, count(id) as count").
Where("created_at >= date_sub(curdate(), interval 6 day) and deleted_at is null").
Group("date(created_at)").
Order("date asc").
Scan(&out.StoresLast7Days).Error; err != nil {
return out, err
}
if err := h.db.WithContext(ctx).
Table("reviews").
Select("stores.id as store_id, stores.name as name, avg(reviews.rating) as avg, count(reviews.id) as count").
Joins("left join stores on stores.id = reviews.store_id").
Where("reviews.status = 'approved' and reviews.deleted_at is null and stores.deleted_at is null").
Group("stores.id, stores.name").
Having("count(reviews.id) >= ?", 3).
Order("avg desc").
Limit(5).
Scan(&out.TopRatedStores).Error; err != nil {
return out, err
}
return out, nil
}