From 4f2b8ea3ad037566ea9526eec4d4e7e0fc0c0d8f Mon Sep 17 00:00:00 2001 From: Rogee Date: Sat, 17 Jan 2026 09:18:05 +0800 Subject: [PATCH] feat: add superadmin health center --- backend/app/http/super/v1/dto/super_health.go | 75 ++++++ backend/app/http/super/v1/healths.go | 29 +++ backend/app/http/super/v1/provider.gen.go | 9 + backend/app/http/super/v1/routes.gen.go | 7 + backend/docs/docs.go | 170 +++++++++++++ backend/docs/swagger.json | 170 +++++++++++++ backend/docs/swagger.yaml | 121 +++++++++ docs/superadmin_plan.md | 7 +- docs/superadmin_progress.md | 11 +- frontend/superadmin/SUPERADMIN_PAGES.md | 10 +- frontend/superadmin/src/layout/AppMenu.vue | 1 + frontend/superadmin/src/router/index.js | 5 + .../superadmin/src/service/HealthService.js | 21 ++ .../src/views/superadmin/HealthOverview.vue | 239 ++++++++++++++++++ 14 files changed, 870 insertions(+), 5 deletions(-) create mode 100644 backend/app/http/super/v1/dto/super_health.go create mode 100644 backend/app/http/super/v1/healths.go create mode 100644 frontend/superadmin/src/service/HealthService.js create mode 100644 frontend/superadmin/src/views/superadmin/HealthOverview.vue diff --git a/backend/app/http/super/v1/dto/super_health.go b/backend/app/http/super/v1/dto/super_health.go new file mode 100644 index 0000000..d9c55f4 --- /dev/null +++ b/backend/app/http/super/v1/dto/super_health.go @@ -0,0 +1,75 @@ +package dto + +// SuperHealthOverviewFilter 超管健康概览查询条件。 +type SuperHealthOverviewFilter struct { + // TenantID 租户ID(不传代表全平台)。 + TenantID *int64 `query:"tenant_id"` + // StartAt 统计开始时间(RFC3339,可选;默认当前时间往前 7 天)。 + StartAt *string `query:"start_at"` + // EndAt 统计结束时间(RFC3339,可选;默认当前时间)。 + EndAt *string `query:"end_at"` + // UploadStuckHours 上传处理超时时长(小时,可选;默认 24 小时)。 + UploadStuckHours *int64 `query:"upload_stuck_hours"` +} + +// SuperHealthOverviewResponse 超管健康概览响应。 +type SuperHealthOverviewResponse struct { + // TenantID 当前统计的租户ID(0 代表全平台)。 + TenantID int64 `json:"tenant_id"` + // StartAt 统计开始时间(RFC3339)。 + StartAt string `json:"start_at"` + // EndAt 统计结束时间(RFC3339)。 + EndAt string `json:"end_at"` + // UploadStuckHours 上传处理超时时长(小时)。 + UploadStuckHours int64 `json:"upload_stuck_hours"` + // TenantTotal 租户总数。 + TenantTotal int64 `json:"tenant_total"` + // TenantWarningCount 健康预警租户数。 + TenantWarningCount int64 `json:"tenant_warning_count"` + // TenantRiskCount 健康风险租户数。 + TenantRiskCount int64 `json:"tenant_risk_count"` + // OrderTotal 订单总数(统计区间内)。 + OrderTotal int64 `json:"order_total"` + // OrderFailed 失败订单数(统计区间内)。 + OrderFailed int64 `json:"order_failed"` + // OrderFailedRate 失败率(失败订单数 / 总订单数)。 + OrderFailedRate float64 `json:"order_failed_rate"` + // UploadTotal 上传资产总数(统计区间内)。 + UploadTotal int64 `json:"upload_total"` + // UploadFailed 上传失败资产数(统计区间内)。 + UploadFailed int64 `json:"upload_failed"` + // UploadFailedRate 上传失败率(失败资产数 / 总资产数)。 + UploadFailedRate float64 `json:"upload_failed_rate"` + // UploadProcessingStuck 处理超时资产数(processing 且超时)。 + UploadProcessingStuck int64 `json:"upload_processing_stuck"` + // UploadUploadedStuck 已上传但未进入处理的超时资产数(uploaded 且超时)。 + UploadUploadedStuck int64 `json:"upload_uploaded_stuck"` + // StorageTotalCount 资产总量(全量)。 + StorageTotalCount int64 `json:"storage_total_count"` + // StorageTotalSize 资产总大小(字节,全量)。 + StorageTotalSize int64 `json:"storage_total_size"` + // Alerts 健康告警列表。 + Alerts []SuperHealthAlertItem `json:"alerts"` +} + +// SuperHealthAlertItem 健康告警条目。 +type SuperHealthAlertItem struct { + // Level 告警级别(warning/risk)。 + Level string `json:"level"` + // Kind 告警类型(order_error_rate/upload_error_rate/upload_stuck/tenant_health)。 + Kind string `json:"kind"` + // TenantID 关联租户ID(无则为 0)。 + TenantID int64 `json:"tenant_id"` + // TenantCode 关联租户编码。 + TenantCode string `json:"tenant_code"` + // TenantName 关联租户名称。 + TenantName string `json:"tenant_name"` + // Title 告警标题。 + Title string `json:"title"` + // Detail 告警详情说明。 + Detail string `json:"detail"` + // Count 关联数量(例如失败数、超时数)。 + Count int64 `json:"count"` + // UpdatedAt 告警更新时间(RFC3339)。 + UpdatedAt string `json:"updated_at"` +} diff --git a/backend/app/http/super/v1/healths.go b/backend/app/http/super/v1/healths.go new file mode 100644 index 0000000..fc16bb0 --- /dev/null +++ b/backend/app/http/super/v1/healths.go @@ -0,0 +1,29 @@ +package v1 + +import ( + dto "quyun/v2/app/http/super/v1/dto" + "quyun/v2/app/services" + + "github.com/gofiber/fiber/v3" +) + +// @provider +type healths struct{} + +// Health overview +// +// @Router /super/v1/health/overview [get] +// @Summary Health overview +// @Description Platform health overview +// @Tags Health +// @Accept json +// @Produce json +// @Param tenant_id query int false "Tenant ID" +// @Param start_at query string false "Start time" +// @Param end_at query string false "End time" +// @Param upload_stuck_hours query int false "Upload stuck hours" +// @Success 200 {object} dto.SuperHealthOverviewResponse +// @Bind filter query +func (c *healths) Overview(ctx fiber.Ctx, filter *dto.SuperHealthOverviewFilter) (*dto.SuperHealthOverviewResponse, error) { + return services.Super.HealthOverview(ctx, filter) +} diff --git a/backend/app/http/super/v1/provider.gen.go b/backend/app/http/super/v1/provider.gen.go index 2c8982e..36b7c1d 100755 --- a/backend/app/http/super/v1/provider.gen.go +++ b/backend/app/http/super/v1/provider.gen.go @@ -73,6 +73,13 @@ func Provide(opts ...opt.Option) error { }); err != nil { return err } + if err := container.Container.Provide(func() (*healths, error) { + obj := &healths{} + + return obj, nil + }); err != nil { + return err + } if err := container.Container.Provide(func() (*notifications, error) { obj := ¬ifications{} @@ -111,6 +118,7 @@ func Provide(opts ...opt.Option) error { creatorApplications *creatorApplications, creators *creators, finance *finance, + healths *healths, middlewares *middlewares.Middlewares, notifications *notifications, orders *orders, @@ -131,6 +139,7 @@ func Provide(opts ...opt.Option) error { creatorApplications: creatorApplications, creators: creators, finance: finance, + healths: healths, middlewares: middlewares, notifications: notifications, orders: orders, diff --git a/backend/app/http/super/v1/routes.gen.go b/backend/app/http/super/v1/routes.gen.go index 8601cfe..0f80dcd 100644 --- a/backend/app/http/super/v1/routes.gen.go +++ b/backend/app/http/super/v1/routes.gen.go @@ -34,6 +34,7 @@ type Routes struct { creatorApplications *creatorApplications creators *creators finance *finance + healths *healths notifications *notifications orders *orders payoutAccounts *payoutAccounts @@ -243,6 +244,12 @@ func (r *Routes) Register(router fiber.Router) { r.finance.ListLedgers, Query[dto.SuperLedgerListFilter]("filter"), )) + // Register routes for controller: healths + r.log.Debugf("Registering route: Get /super/v1/health/overview -> healths.Overview") + router.Get("/super/v1/health/overview"[len(r.Path()):], DataFunc1( + r.healths.Overview, + Query[dto.SuperHealthOverviewFilter]("filter"), + )) // Register routes for controller: notifications r.log.Debugf("Registering route: Get /super/v1/notifications -> notifications.List") router.Get("/super/v1/notifications"[len(r.Path()):], DataFunc1( diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 7c225a2..4e1a684 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -1153,6 +1153,55 @@ const docTemplate = `{ } } }, + "/super/v1/health/overview": { + "get": { + "description": "Platform health overview", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Health" + ], + "summary": "Health overview", + "parameters": [ + { + "type": "integer", + "description": "Tenant ID", + "name": "tenant_id", + "in": "query" + }, + { + "type": "string", + "description": "Start time", + "name": "start_at", + "in": "query" + }, + { + "type": "string", + "description": "End time", + "name": "end_at", + "in": "query" + }, + { + "type": "integer", + "description": "Upload stuck hours", + "name": "upload_stuck_hours", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.SuperHealthOverviewResponse" + } + } + } + } + }, "/super/v1/notifications": { "get": { "description": "List notifications across tenants", @@ -8669,6 +8718,127 @@ const docTemplate = `{ } } }, + "dto.SuperHealthAlertItem": { + "type": "object", + "properties": { + "count": { + "description": "Count 关联数量(例如失败数、超时数)。", + "type": "integer" + }, + "detail": { + "description": "Detail 告警详情说明。", + "type": "string" + }, + "kind": { + "description": "Kind 告警类型(order_error_rate/upload_error_rate/upload_stuck/tenant_health)。", + "type": "string" + }, + "level": { + "description": "Level 告警级别(warning/risk)。", + "type": "string" + }, + "tenant_code": { + "description": "TenantCode 关联租户编码。", + "type": "string" + }, + "tenant_id": { + "description": "TenantID 关联租户ID(无则为 0)。", + "type": "integer" + }, + "tenant_name": { + "description": "TenantName 关联租户名称。", + "type": "string" + }, + "title": { + "description": "Title 告警标题。", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt 告警更新时间(RFC3339)。", + "type": "string" + } + } + }, + "dto.SuperHealthOverviewResponse": { + "type": "object", + "properties": { + "alerts": { + "description": "Alerts 健康告警列表。", + "type": "array", + "items": { + "$ref": "#/definitions/dto.SuperHealthAlertItem" + } + }, + "end_at": { + "description": "EndAt 统计结束时间(RFC3339)。", + "type": "string" + }, + "order_failed": { + "description": "OrderFailed 失败订单数(统计区间内)。", + "type": "integer" + }, + "order_failed_rate": { + "description": "OrderFailedRate 失败率(失败订单数 / 总订单数)。", + "type": "number" + }, + "order_total": { + "description": "OrderTotal 订单总数(统计区间内)。", + "type": "integer" + }, + "start_at": { + "description": "StartAt 统计开始时间(RFC3339)。", + "type": "string" + }, + "storage_total_count": { + "description": "StorageTotalCount 资产总量(全量)。", + "type": "integer" + }, + "storage_total_size": { + "description": "StorageTotalSize 资产总大小(字节,全量)。", + "type": "integer" + }, + "tenant_id": { + "description": "TenantID 当前统计的租户ID(0 代表全平台)。", + "type": "integer" + }, + "tenant_risk_count": { + "description": "TenantRiskCount 健康风险租户数。", + "type": "integer" + }, + "tenant_total": { + "description": "TenantTotal 租户总数。", + "type": "integer" + }, + "tenant_warning_count": { + "description": "TenantWarningCount 健康预警租户数。", + "type": "integer" + }, + "upload_failed": { + "description": "UploadFailed 上传失败资产数(统计区间内)。", + "type": "integer" + }, + "upload_failed_rate": { + "description": "UploadFailedRate 上传失败率(失败资产数 / 总资产数)。", + "type": "number" + }, + "upload_processing_stuck": { + "description": "UploadProcessingStuck 处理超时资产数(processing 且超时)。", + "type": "integer" + }, + "upload_stuck_hours": { + "description": "UploadStuckHours 上传处理超时时长(小时)。", + "type": "integer" + }, + "upload_total": { + "description": "UploadTotal 上传资产总数(统计区间内)。", + "type": "integer" + }, + "upload_uploaded_stuck": { + "description": "UploadUploadedStuck 已上传但未进入处理的超时资产数(uploaded 且超时)。", + "type": "integer" + } + } + }, "dto.SuperLedgerItem": { "type": "object", "properties": { diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index 998681d..7eaa68e 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -1147,6 +1147,55 @@ } } }, + "/super/v1/health/overview": { + "get": { + "description": "Platform health overview", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Health" + ], + "summary": "Health overview", + "parameters": [ + { + "type": "integer", + "description": "Tenant ID", + "name": "tenant_id", + "in": "query" + }, + { + "type": "string", + "description": "Start time", + "name": "start_at", + "in": "query" + }, + { + "type": "string", + "description": "End time", + "name": "end_at", + "in": "query" + }, + { + "type": "integer", + "description": "Upload stuck hours", + "name": "upload_stuck_hours", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.SuperHealthOverviewResponse" + } + } + } + } + }, "/super/v1/notifications": { "get": { "description": "List notifications across tenants", @@ -8663,6 +8712,127 @@ } } }, + "dto.SuperHealthAlertItem": { + "type": "object", + "properties": { + "count": { + "description": "Count 关联数量(例如失败数、超时数)。", + "type": "integer" + }, + "detail": { + "description": "Detail 告警详情说明。", + "type": "string" + }, + "kind": { + "description": "Kind 告警类型(order_error_rate/upload_error_rate/upload_stuck/tenant_health)。", + "type": "string" + }, + "level": { + "description": "Level 告警级别(warning/risk)。", + "type": "string" + }, + "tenant_code": { + "description": "TenantCode 关联租户编码。", + "type": "string" + }, + "tenant_id": { + "description": "TenantID 关联租户ID(无则为 0)。", + "type": "integer" + }, + "tenant_name": { + "description": "TenantName 关联租户名称。", + "type": "string" + }, + "title": { + "description": "Title 告警标题。", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt 告警更新时间(RFC3339)。", + "type": "string" + } + } + }, + "dto.SuperHealthOverviewResponse": { + "type": "object", + "properties": { + "alerts": { + "description": "Alerts 健康告警列表。", + "type": "array", + "items": { + "$ref": "#/definitions/dto.SuperHealthAlertItem" + } + }, + "end_at": { + "description": "EndAt 统计结束时间(RFC3339)。", + "type": "string" + }, + "order_failed": { + "description": "OrderFailed 失败订单数(统计区间内)。", + "type": "integer" + }, + "order_failed_rate": { + "description": "OrderFailedRate 失败率(失败订单数 / 总订单数)。", + "type": "number" + }, + "order_total": { + "description": "OrderTotal 订单总数(统计区间内)。", + "type": "integer" + }, + "start_at": { + "description": "StartAt 统计开始时间(RFC3339)。", + "type": "string" + }, + "storage_total_count": { + "description": "StorageTotalCount 资产总量(全量)。", + "type": "integer" + }, + "storage_total_size": { + "description": "StorageTotalSize 资产总大小(字节,全量)。", + "type": "integer" + }, + "tenant_id": { + "description": "TenantID 当前统计的租户ID(0 代表全平台)。", + "type": "integer" + }, + "tenant_risk_count": { + "description": "TenantRiskCount 健康风险租户数。", + "type": "integer" + }, + "tenant_total": { + "description": "TenantTotal 租户总数。", + "type": "integer" + }, + "tenant_warning_count": { + "description": "TenantWarningCount 健康预警租户数。", + "type": "integer" + }, + "upload_failed": { + "description": "UploadFailed 上传失败资产数(统计区间内)。", + "type": "integer" + }, + "upload_failed_rate": { + "description": "UploadFailedRate 上传失败率(失败资产数 / 总资产数)。", + "type": "number" + }, + "upload_processing_stuck": { + "description": "UploadProcessingStuck 处理超时资产数(processing 且超时)。", + "type": "integer" + }, + "upload_stuck_hours": { + "description": "UploadStuckHours 上传处理超时时长(小时)。", + "type": "integer" + }, + "upload_total": { + "description": "UploadTotal 上传资产总数(统计区间内)。", + "type": "integer" + }, + "upload_uploaded_stuck": { + "description": "UploadUploadedStuck 已上传但未进入处理的超时资产数(uploaded 且超时)。", + "type": "integer" + } + } + }, "dto.SuperLedgerItem": { "type": "object", "properties": { diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index e991e95..93e4ff1 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -1762,6 +1762,95 @@ definitions: required: - action type: object + dto.SuperHealthAlertItem: + properties: + count: + description: Count 关联数量(例如失败数、超时数)。 + type: integer + detail: + description: Detail 告警详情说明。 + type: string + kind: + description: Kind 告警类型(order_error_rate/upload_error_rate/upload_stuck/tenant_health)。 + type: string + level: + description: Level 告警级别(warning/risk)。 + type: string + tenant_code: + description: TenantCode 关联租户编码。 + type: string + tenant_id: + description: TenantID 关联租户ID(无则为 0)。 + type: integer + tenant_name: + description: TenantName 关联租户名称。 + type: string + title: + description: Title 告警标题。 + type: string + updated_at: + description: UpdatedAt 告警更新时间(RFC3339)。 + type: string + type: object + dto.SuperHealthOverviewResponse: + properties: + alerts: + description: Alerts 健康告警列表。 + items: + $ref: '#/definitions/dto.SuperHealthAlertItem' + type: array + end_at: + description: EndAt 统计结束时间(RFC3339)。 + type: string + order_failed: + description: OrderFailed 失败订单数(统计区间内)。 + type: integer + order_failed_rate: + description: OrderFailedRate 失败率(失败订单数 / 总订单数)。 + type: number + order_total: + description: OrderTotal 订单总数(统计区间内)。 + type: integer + start_at: + description: StartAt 统计开始时间(RFC3339)。 + type: string + storage_total_count: + description: StorageTotalCount 资产总量(全量)。 + type: integer + storage_total_size: + description: StorageTotalSize 资产总大小(字节,全量)。 + type: integer + tenant_id: + description: TenantID 当前统计的租户ID(0 代表全平台)。 + type: integer + tenant_risk_count: + description: TenantRiskCount 健康风险租户数。 + type: integer + tenant_total: + description: TenantTotal 租户总数。 + type: integer + tenant_warning_count: + description: TenantWarningCount 健康预警租户数。 + type: integer + upload_failed: + description: UploadFailed 上传失败资产数(统计区间内)。 + type: integer + upload_failed_rate: + description: UploadFailedRate 上传失败率(失败资产数 / 总资产数)。 + type: number + upload_processing_stuck: + description: UploadProcessingStuck 处理超时资产数(processing 且超时)。 + type: integer + upload_stuck_hours: + description: UploadStuckHours 上传处理超时时长(小时)。 + type: integer + upload_total: + description: UploadTotal 上传资产总数(统计区间内)。 + type: integer + upload_uploaded_stuck: + description: UploadUploadedStuck 已上传但未进入处理的超时资产数(uploaded 且超时)。 + type: integer + type: object dto.SuperLedgerItem: properties: amount: @@ -4064,6 +4153,38 @@ paths: summary: List ledgers tags: - Finance + /super/v1/health/overview: + get: + consumes: + - application/json + description: Platform health overview + parameters: + - description: Tenant ID + in: query + name: tenant_id + type: integer + - description: Start time + in: query + name: start_at + type: string + - description: End time + in: query + name: end_at + type: string + - description: Upload stuck hours + in: query + name: upload_stuck_hours + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.SuperHealthOverviewResponse' + summary: Health overview + tags: + - Health /super/v1/notifications: get: consumes: diff --git a/docs/superadmin_plan.md b/docs/superadmin_plan.md index 90aaefe..bb4e66e 100644 --- a/docs/superadmin_plan.md +++ b/docs/superadmin_plan.md @@ -87,6 +87,11 @@ - **接口**:用户侧 `GET /t/:tenantCode/v1/me/notifications`。 - **缺口**:超管发送与批量触达接口、通知模板管理接口。 +### 2.15 健康与告警 `/superadmin/health` +- **功能**:平台健康总览、失败率/超时/风险租户聚合、告警列表。 +- **接口**:`GET /super/v1/health/overview`、`GET /super/v1/tenants/health`(租户级健康明细)。 +- **缺口**:后续可补充告警订阅与自动化处理规则。 + ## 3) 导航与便捷性设计(建议) - 统一筛选器:租户/用户/时间/状态为默认筛选维度。 @@ -97,4 +102,4 @@ - **P0(已有接口即可落地)**:登录、概览、租户管理、用户管理、内容治理、订单退款。 - **P1(需补超管接口)**:创作者审核、优惠券、提现/钱包、报表导出。 -- **P2(扩展增强)**:资产/上传、通知中心、审计与系统配置。 +- **P2(扩展增强)**:资产/上传、通知中心、审计与系统配置、健康与告警。 diff --git a/docs/superadmin_progress.md b/docs/superadmin_progress.md index 5840de8..98d861b 100644 --- a/docs/superadmin_progress.md +++ b/docs/superadmin_progress.md @@ -4,7 +4,7 @@ ## 1) 总体结论 -- **已落地**:登录、租户/用户/订单/内容基础管理、内容审核(含批量)、平台概览(内容趋势/退款率/漏斗)、提现审核、钱包流水与异常排查、报表概览与导出、用户钱包/通知/优惠券/实名/充值记录、互动(收藏/点赞/关注)与内容消费明细视图、创作者申请/成员审核/邀请、优惠券创建/编辑/发放/冻结/发放记录/异常核查、资产治理(列表/用量/清理)、通知中心(列表/群发/模板)、审计日志与系统配置、评论治理。 +- **已落地**:登录、租户/用户/订单/内容基础管理、内容审核(含批量)、平台概览(内容趋势/退款率/漏斗)、提现审核、钱包流水与异常排查、报表概览与导出、用户钱包/通知/优惠券/实名/充值记录、互动(收藏/点赞/关注)与内容消费明细视图、创作者申请/成员审核/邀请、优惠券创建/编辑/发放/冻结/发放记录/异常核查、资产治理(列表/用量/清理)、通知中心(列表/群发/模板)、健康与告警中心、审计日志与系统配置、评论治理。 - **部分落地**:暂无。 - **未落地**:暂无。 @@ -80,14 +80,19 @@ - 已有:通知列表、批量发送、模板管理。 - 缺口:无显著功能缺口。 -### 2.15 审计与系统配置 `/superadmin/audit-logs` `/superadmin/system-configs` +### 2.15 健康与告警 `/superadmin/health` +- 状态:**已完成** +- 已有:平台健康概览、失败率/超时统计、风险租户告警列表。 +- 缺口:无显著功能缺口。 + +### 2.16 审计与系统配置 `/superadmin/audit-logs` `/superadmin/system-configs` - 状态:**已完成** - 已有:跨租户审计日志查询、系统配置列表/创建/更新。 - 缺口:无显著功能缺口。 ## 3) `/super/v1` 接口覆盖度概览 -- **已具备**:Auth、Tenants(含成员审核/邀请)、Users(含钱包/通知/优惠券/实名/互动/内容消费)、Contents(含评论治理)、Orders、Withdrawals、Finance(流水/异常)、Reports、Coupons(列表/创建/编辑/发放/冻结/记录)、Creators(列表/申请/成员审核)、Payout Accounts(列表/删除)、Assets(列表/用量/删除)、Notifications(列表/群发/模板)。 +- **已具备**:Auth、Tenants(含成员审核/邀请)、Users(含钱包/通知/优惠券/实名/互动/内容消费)、Contents(含评论治理)、Orders、Withdrawals、Finance(流水/异常)、Reports、Coupons(列表/创建/编辑/发放/冻结/记录)、Creators(列表/申请/成员审核)、Payout Accounts(列表/删除)、Assets(列表/用量/删除)、Notifications(列表/群发/模板)、Health(平台健康概览)。 - **缺失/待补**:无。 ## 4) 建议的下一步(按优先级) diff --git a/frontend/superadmin/SUPERADMIN_PAGES.md b/frontend/superadmin/SUPERADMIN_PAGES.md index cf93546..674ac3a 100644 --- a/frontend/superadmin/SUPERADMIN_PAGES.md +++ b/frontend/superadmin/SUPERADMIN_PAGES.md @@ -32,12 +32,13 @@ - `/superadmin/reports`:报表与导出(待补超管接口) - `/superadmin/assets`:资产与上传(待补超管接口) - `/superadmin/notifications`:通知与消息(待补超管接口) +- `/superadmin/health`:健康与告警中心 ## 1.1 迭代路线(按接口可用性) - **P0(已有接口可落地)**:登录、概览、租户管理/详情、用户管理/详情、订单列表/详情/退款、内容治理。 - **P1(需补超管接口)**:创作者审核、优惠券、财务/提现、报表导出。 -- **P2(扩展增强)**:资产/上传治理、通知中心、审计日志。 +- **P2(扩展增强)**:资产/上传治理、通知中心、审计日志、健康与告警。 ## 2. 页面规格(页面 → 功能 → API) @@ -188,6 +189,13 @@ - `GET /super/v1/notifications/templates` - `POST /super/v1/notifications/templates` +### 2.16 健康与告警 `/superadmin/health` + +- 平台健康总览、失败率/超时统计、风险租户告警列表。 +- API: + - `GET /super/v1/health/overview` + - `GET /super/v1/tenants/health` + ## 3. 枚举与数据结构(UI 需要) - 租户状态:`consts.TenantStatus`(`pending_verify` / `verified` / `banned`),推荐用 `GET /super/v1/tenants/statuses` 驱动展示 label diff --git a/frontend/superadmin/src/layout/AppMenu.vue b/frontend/superadmin/src/layout/AppMenu.vue index 3b2e9a4..e826df1 100644 --- a/frontend/superadmin/src/layout/AppMenu.vue +++ b/frontend/superadmin/src/layout/AppMenu.vue @@ -12,6 +12,7 @@ const model = ref([ label: 'Super Admin', items: [ { label: 'Tenants', icon: 'pi pi-fw pi-building', to: '/superadmin/tenants' }, + { label: 'Health Center', icon: 'pi pi-fw pi-heart', to: '/superadmin/health' }, { label: 'Users', icon: 'pi pi-fw pi-users', to: '/superadmin/users' }, { label: 'Orders', icon: 'pi pi-fw pi-shopping-cart', to: '/superadmin/orders' }, { label: 'Contents', icon: 'pi pi-fw pi-file', to: '/superadmin/contents' }, diff --git a/frontend/superadmin/src/router/index.js b/frontend/superadmin/src/router/index.js index a23b262..8cd2cd4 100644 --- a/frontend/superadmin/src/router/index.js +++ b/frontend/superadmin/src/router/index.js @@ -119,6 +119,11 @@ const router = createRouter({ name: 'superadmin-tenants', component: () => import('@/views/superadmin/Tenants.vue') }, + { + path: '/superadmin/health', + name: 'superadmin-health', + component: () => import('@/views/superadmin/HealthOverview.vue') + }, { path: '/superadmin/tenants/:tenantID', name: 'superadmin-tenant-detail', diff --git a/frontend/superadmin/src/service/HealthService.js b/frontend/superadmin/src/service/HealthService.js new file mode 100644 index 0000000..076254a --- /dev/null +++ b/frontend/superadmin/src/service/HealthService.js @@ -0,0 +1,21 @@ +import { requestJson } from './apiClient'; + +export const HealthService = { + async getOverview({ tenant_id, start_at, end_at, upload_stuck_hours } = {}) { + const iso = (d) => { + if (!d) return undefined; + const date = d instanceof Date ? d : new Date(d); + if (Number.isNaN(date.getTime())) return undefined; + return date.toISOString(); + }; + + const query = { + tenant_id, + start_at: iso(start_at), + end_at: iso(end_at), + upload_stuck_hours + }; + + return requestJson('/super/v1/health/overview', { query }); + } +}; diff --git a/frontend/superadmin/src/views/superadmin/HealthOverview.vue b/frontend/superadmin/src/views/superadmin/HealthOverview.vue new file mode 100644 index 0000000..40ff917 --- /dev/null +++ b/frontend/superadmin/src/views/superadmin/HealthOverview.vue @@ -0,0 +1,239 @@ + + +