feat: enhance superadmin dashboard overview

This commit is contained in:
2026-01-15 14:16:20 +08:00
parent a8453e0c6c
commit c683fa5cf3
10 changed files with 449 additions and 10 deletions

View File

@@ -50,6 +50,20 @@ func (c *contents) ListTenantContents(ctx fiber.Ctx, tenantID int64, filter *dto
return services.Super.ListContents(ctx, filter)
}
// Content statistics
//
// @Router /super/v1/contents/statistics [get]
// @Summary Content statistics
// @Description Content statistics
// @Tags Content
// @Accept json
// @Produce json
// @Success 200 {object} dto.SuperContentStatisticsResponse
// @Bind filter query
func (c *contents) Statistics(ctx fiber.Ctx, filter *dto.SuperContentStatisticsFilter) (*dto.SuperContentStatisticsResponse, error) {
return services.Super.ContentStatistics(ctx, filter)
}
// Update content status
//
// @Router /super/v1/tenants/:tenantID<int>/contents/:contentID<int>/status [patch]

View File

@@ -131,6 +131,18 @@ type SuperContentListFilter struct {
Desc *string `query:"desc"`
}
// SuperContentStatisticsFilter 超管内容统计查询条件。
type SuperContentStatisticsFilter struct {
// TenantID 租户ID不传代表全平台
TenantID *int64 `query:"tenant_id"`
// StartAt 统计开始时间RFC3339可选默认当前时间往前 7 天)。
StartAt *string `query:"start_at"`
// EndAt 统计结束时间RFC3339可选默认当前时间
EndAt *string `query:"end_at"`
// Granularity 统计粒度day目前仅支持 day
Granularity *string `query:"granularity"`
}
type SuperOrderListFilter struct {
requests.Pagination
// ID 订单ID精确匹配。
@@ -635,3 +647,19 @@ type AdminContentOwnerLite struct {
// Status 用户状态。
Status consts.UserStatus `json:"status"`
}
// SuperContentStatisticsResponse 超管内容统计响应。
type SuperContentStatisticsResponse struct {
// TotalCount 内容总量。
TotalCount int64 `json:"total_count"`
// Trend 按天新增内容趋势。
Trend []SuperContentTrendItem `json:"trend"`
}
// SuperContentTrendItem 内容新增趋势条目。
type SuperContentTrendItem struct {
// Date 日期YYYY-MM-DD
Date string `json:"date"`
// CreatedCount 当日新增内容数量。
CreatedCount int64 `json:"created_count"`
}

View File

@@ -58,6 +58,11 @@ func (r *Routes) Register(router fiber.Router) {
r.contents.List,
Query[dto.SuperContentListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /super/v1/contents/statistics -> contents.Statistics")
router.Get("/super/v1/contents/statistics"[len(r.Path()):], DataFunc1(
r.contents.Statistics,
Query[dto.SuperContentStatisticsFilter]("filter"),
))
r.log.Debugf("Registering route: Get /super/v1/tenants/:tenantID<int>/contents -> contents.ListTenantContents")
router.Get("/super/v1/tenants/:tenantID<int>/contents"[len(r.Path()):], DataFunc2(
r.contents.ListTenantContents,

View File

@@ -2059,6 +2059,74 @@ func (s *super) BatchReviewContents(ctx context.Context, operatorID int64, form
return nil
}
func (s *super) ContentStatistics(ctx context.Context, filter *super_dto.SuperContentStatisticsFilter) (*super_dto.SuperContentStatisticsResponse, error) {
// 统一统计时间范围与粒度,默认最近 7 天。
reportFilter := &super_dto.SuperReportOverviewFilter{}
if filter != nil {
reportFilter.TenantID = filter.TenantID
reportFilter.StartAt = filter.StartAt
reportFilter.EndAt = filter.EndAt
reportFilter.Granularity = filter.Granularity
}
rg, err := s.normalizeReportRange(reportFilter)
if err != nil {
return nil, err
}
tenantID := int64(0)
if filter != nil && filter.TenantID != nil {
tenantID = *filter.TenantID
}
// 统计内容总量,支持租户维度过滤。
tbl, q := models.ContentQuery.QueryContext(ctx)
if tenantID > 0 {
q = q.Where(tbl.TenantID.Eq(tenantID))
}
total, err := q.Count()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
// 按天聚合新增内容数量,补齐趋势序列。
type contentAggRow struct {
Day time.Time `gorm:"column:day"`
Count int64 `gorm:"column:count"`
}
rows := make([]contentAggRow, 0)
query := models.ContentQuery.WithContext(ctx).
UnderlyingDB().
Model(&models.Content{}).
Select("date_trunc('day', created_at) as day, count(*) as count").
Where("created_at >= ? AND created_at < ?", rg.startDay, rg.endNext)
if tenantID > 0 {
query = query.Where("tenant_id = ?", tenantID)
}
if err := query.Group("day").Scan(&rows).Error; err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
trendMap := make(map[string]int64, len(rows))
for _, row := range rows {
key := row.Day.Format("2006-01-02")
trendMap[key] = row.Count
}
trend := make([]super_dto.SuperContentTrendItem, 0)
for day := rg.startDay; !day.After(rg.endDay); day = day.AddDate(0, 0, 1) {
key := day.Format("2006-01-02")
trend = append(trend, super_dto.SuperContentTrendItem{
Date: key,
CreatedCount: trendMap[key],
})
}
return &super_dto.SuperContentStatisticsResponse{
TotalCount: total,
Trend: trend,
}, nil
}
func (s *super) ListOrders(ctx context.Context, filter *super_dto.SuperOrderListFilter) (*requests.Pager, error) {
tbl, q := models.OrderQuery.QueryContext(ctx)

View File

@@ -167,6 +167,29 @@ const docTemplate = `{
}
}
},
"/super/v1/contents/statistics": {
"get": {
"description": "Content statistics",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Content"
],
"summary": "Content statistics",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.SuperContentStatisticsResponse"
}
}
}
}
},
"/super/v1/contents/{id}/review": {
"post": {
"description": "Review content",
@@ -6374,6 +6397,22 @@ const docTemplate = `{
}
}
},
"dto.SuperContentStatisticsResponse": {
"type": "object",
"properties": {
"total_count": {
"description": "TotalCount 内容总量。",
"type": "integer"
},
"trend": {
"description": "Trend 按天新增内容趋势。",
"type": "array",
"items": {
"$ref": "#/definitions/dto.SuperContentTrendItem"
}
}
}
},
"dto.SuperContentTenantLite": {
"type": "object",
"properties": {
@@ -6391,6 +6430,19 @@ const docTemplate = `{
}
}
},
"dto.SuperContentTrendItem": {
"type": "object",
"properties": {
"created_count": {
"description": "CreatedCount 当日新增内容数量。",
"type": "integer"
},
"date": {
"description": "Date 日期YYYY-MM-DD。",
"type": "string"
}
}
},
"dto.SuperCouponGrantItem": {
"type": "object",
"properties": {

View File

@@ -161,6 +161,29 @@
}
}
},
"/super/v1/contents/statistics": {
"get": {
"description": "Content statistics",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Content"
],
"summary": "Content statistics",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.SuperContentStatisticsResponse"
}
}
}
}
},
"/super/v1/contents/{id}/review": {
"post": {
"description": "Review content",
@@ -6368,6 +6391,22 @@
}
}
},
"dto.SuperContentStatisticsResponse": {
"type": "object",
"properties": {
"total_count": {
"description": "TotalCount 内容总量。",
"type": "integer"
},
"trend": {
"description": "Trend 按天新增内容趋势。",
"type": "array",
"items": {
"$ref": "#/definitions/dto.SuperContentTrendItem"
}
}
}
},
"dto.SuperContentTenantLite": {
"type": "object",
"properties": {
@@ -6385,6 +6424,19 @@
}
}
},
"dto.SuperContentTrendItem": {
"type": "object",
"properties": {
"created_count": {
"description": "CreatedCount 当日新增内容数量。",
"type": "integer"
},
"date": {
"description": "Date 日期YYYY-MM-DD。",
"type": "string"
}
}
},
"dto.SuperCouponGrantItem": {
"type": "object",
"properties": {

View File

@@ -1048,6 +1048,17 @@ definitions:
required:
- action
type: object
dto.SuperContentStatisticsResponse:
properties:
total_count:
description: TotalCount 内容总量。
type: integer
trend:
description: Trend 按天新增内容趋势。
items:
$ref: '#/definitions/dto.SuperContentTrendItem'
type: array
type: object
dto.SuperContentTenantLite:
properties:
code:
@@ -1060,6 +1071,15 @@ definitions:
description: Name 租户名称。
type: string
type: object
dto.SuperContentTrendItem:
properties:
created_count:
description: CreatedCount 当日新增内容数量。
type: integer
date:
description: Date 日期YYYY-MM-DD
type: string
type: object
dto.SuperCouponGrantItem:
properties:
coupon_id:
@@ -2500,6 +2520,21 @@ paths:
summary: Batch review contents
tags:
- Content
/super/v1/contents/statistics:
get:
consumes:
- application/json
description: Content statistics
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.SuperContentStatisticsResponse'
summary: Content statistics
tags:
- Content
/super/v1/coupon-grants:
get:
consumes: