From 62771583e7c26014afeaf3cedb2657f593f5c3c6 Mon Sep 17 00:00:00 2001 From: IanShaw027 <131567472+IanShaw027@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:17:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(ops):=20=E9=9B=86=E6=88=90=E5=AE=9E?= =?UTF-8?q?=E6=97=B6=E6=B5=81=E9=87=8FAPI=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加实时流量handler处理逻辑 - 注册实时流量路由 - 扩展ops service接口定义 --- .../handler/admin/ops_realtime_handler.go | 93 +++++++++++++++++++ backend/internal/server/routes/admin.go | 1 + backend/internal/service/ops_port.go | 2 + 3 files changed, 96 insertions(+) diff --git a/backend/internal/handler/admin/ops_realtime_handler.go b/backend/internal/handler/admin/ops_realtime_handler.go index 0c23c13b..4f15ec57 100644 --- a/backend/internal/handler/admin/ops_realtime_handler.go +++ b/backend/internal/handler/admin/ops_realtime_handler.go @@ -118,3 +118,96 @@ func (h *OpsHandler) GetAccountAvailability(c *gin.Context) { } response.Success(c, payload) } + +func parseOpsRealtimeWindow(v string) (time.Duration, string, bool) { + switch strings.ToLower(strings.TrimSpace(v)) { + case "", "1min", "1m": + return 1 * time.Minute, "1min", true + case "5min", "5m": + return 5 * time.Minute, "5min", true + case "30min", "30m": + return 30 * time.Minute, "30min", true + case "1h", "60m", "60min": + return 1 * time.Hour, "1h", true + default: + return 0, "", false + } +} + +// GetRealtimeTrafficSummary returns QPS/TPS current/peak/avg for the selected window. +// GET /api/v1/admin/ops/realtime-traffic +// +// Query params: +// - window: 1min|5min|30min|1h (default: 1min) +// - platform: optional +// - group_id: optional +func (h *OpsHandler) GetRealtimeTrafficSummary(c *gin.Context) { + if h.opsService == nil { + response.Error(c, http.StatusServiceUnavailable, "Ops service not available") + return + } + if err := h.opsService.RequireMonitoringEnabled(c.Request.Context()); err != nil { + response.ErrorFrom(c, err) + return + } + + windowDur, windowLabel, ok := parseOpsRealtimeWindow(c.Query("window")) + if !ok { + response.BadRequest(c, "Invalid window") + return + } + + platform := strings.TrimSpace(c.Query("platform")) + var groupID *int64 + if v := strings.TrimSpace(c.Query("group_id")); v != "" { + id, err := strconv.ParseInt(v, 10, 64) + if err != nil || id <= 0 { + response.BadRequest(c, "Invalid group_id") + return + } + groupID = &id + } + + endTime := time.Now().UTC() + startTime := endTime.Add(-windowDur) + + if !h.opsService.IsRealtimeMonitoringEnabled(c.Request.Context()) { + disabledSummary := &service.OpsRealtimeTrafficSummary{ + Window: windowLabel, + StartTime: startTime, + EndTime: endTime, + Platform: platform, + GroupID: groupID, + QPS: service.OpsRateSummary{}, + TPS: service.OpsRateSummary{}, + } + response.Success(c, gin.H{ + "enabled": false, + "summary": disabledSummary, + "timestamp": endTime, + }) + return + } + + filter := &service.OpsDashboardFilter{ + StartTime: startTime, + EndTime: endTime, + Platform: platform, + GroupID: groupID, + QueryMode: service.OpsQueryModeRaw, + } + + summary, err := h.opsService.GetRealtimeTrafficSummary(c.Request.Context(), filter) + if err != nil { + response.ErrorFrom(c, err) + return + } + if summary != nil { + summary.Window = windowLabel + } + response.Success(c, gin.H{ + "enabled": true, + "summary": summary, + "timestamp": endTime, + }) +} diff --git a/backend/internal/server/routes/admin.go b/backend/internal/server/routes/admin.go index 98d621c0..ac0e7635 100644 --- a/backend/internal/server/routes/admin.go +++ b/backend/internal/server/routes/admin.go @@ -73,6 +73,7 @@ func registerOpsRoutes(admin *gin.RouterGroup, h *handler.Handlers) { // Realtime ops signals ops.GET("/concurrency", h.Admin.Ops.GetConcurrencyStats) ops.GET("/account-availability", h.Admin.Ops.GetAccountAvailability) + ops.GET("/realtime-traffic", h.Admin.Ops.GetRealtimeTrafficSummary) // Alerts (rules + events) ops.GET("/alert-rules", h.Admin.Ops.ListAlertRules) diff --git a/backend/internal/service/ops_port.go b/backend/internal/service/ops_port.go index 39f3aaf2..4549214d 100644 --- a/backend/internal/service/ops_port.go +++ b/backend/internal/service/ops_port.go @@ -17,6 +17,8 @@ type OpsRepository interface { // Lightweight window stats (for realtime WS / quick sampling). GetWindowStats(ctx context.Context, filter *OpsDashboardFilter) (*OpsWindowStats, error) + // Lightweight realtime traffic summary (for the Ops dashboard header card). + GetRealtimeTrafficSummary(ctx context.Context, filter *OpsDashboardFilter) (*OpsRealtimeTrafficSummary, error) GetDashboardOverview(ctx context.Context, filter *OpsDashboardFilter) (*OpsDashboardOverview, error) GetThroughputTrend(ctx context.Context, filter *OpsDashboardFilter, bucketSeconds int) (*OpsThroughputTrendResponse, error)