From dda8f78733958495d10b4ff17510df6aea83426c Mon Sep 17 00:00:00 2001 From: li Date: Fri, 10 Jul 2026 11:43:44 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(admin):=20GetUserBreakdown=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20ParseUsageRequestType=20=E8=A7=A3=E6=9E=90=20reques?= =?UTF-8?q?t=5Ftype?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #3920 --- .../handler/admin/dashboard_handler.go | 11 ++++-- .../dashboard_handler_user_breakdown_test.go | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/backend/internal/handler/admin/dashboard_handler.go b/backend/internal/handler/admin/dashboard_handler.go index b42b395d33..8f55fb4165 100644 --- a/backend/internal/handler/admin/dashboard_handler.go +++ b/backend/internal/handler/admin/dashboard_handler.go @@ -657,11 +657,14 @@ func (h *DashboardHandler) GetUserBreakdown(c *gin.Context) { dim.AccountID = id } } - if v := c.Query("request_type"); v != "" { - if rt, err := strconv.ParseInt(v, 10, 16); err == nil { - rtVal := int16(rt) - dim.RequestType = &rtVal + if v := strings.TrimSpace(c.Query("request_type")); v != "" { + parsed, err := service.ParseUsageRequestType(v) + if err != nil { + response.BadRequest(c, err.Error()) + return } + rtVal := int16(parsed) + dim.RequestType = &rtVal } if v := c.Query("stream"); v != "" { if s, err := strconv.ParseBool(v); err == nil { diff --git a/backend/internal/handler/admin/dashboard_handler_user_breakdown_test.go b/backend/internal/handler/admin/dashboard_handler_user_breakdown_test.go index 3065eee3c4..2381364a94 100644 --- a/backend/internal/handler/admin/dashboard_handler_user_breakdown_test.go +++ b/backend/internal/handler/admin/dashboard_handler_user_breakdown_test.go @@ -241,3 +241,42 @@ func TestGetUserBreakdown_NoFilters(t *testing.T) { require.Empty(t, repo.capturedDim.Model) require.Empty(t, repo.capturedDim.Endpoint) } + +func TestGetUserBreakdown_RequestTypeStringFilter(t *testing.T) { + cases := []struct { + name string + value string + want int16 + }{ + {"ws_v2", "ws_v2", int16(service.RequestTypeWSV2)}, + {"stream", "stream", int16(service.RequestTypeStream)}, + {"sync", "sync", int16(service.RequestTypeSync)}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + repo := &userBreakdownRepoCapture{} + router := newUserBreakdownRouter(repo) + + req := httptest.NewRequest(http.MethodGet, + "/admin/dashboard/user-breakdown?start_date=2026-03-01&end_date=2026-03-16&request_type="+tc.value, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + require.Equal(t, http.StatusOK, w.Code) + require.NotNil(t, repo.capturedDim.RequestType, "request_type=%s should set filter", tc.value) + require.Equal(t, tc.want, *repo.capturedDim.RequestType) + }) + } +} + +func TestGetUserBreakdown_InvalidRequestType(t *testing.T) { + repo := &userBreakdownRepoCapture{} + router := newUserBreakdownRouter(repo) + + req := httptest.NewRequest(http.MethodGet, + "/admin/dashboard/user-breakdown?start_date=2026-03-01&end_date=2026-03-16&request_type=bogus", nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + require.Equal(t, http.StatusBadRequest, w.Code) +} From ea9f40b63fff8a98ccfab7e1f2f51feab3bdf95d Mon Sep 17 00:00:00 2001 From: li Date: Fri, 10 Jul 2026 12:38:21 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(frontend):=20UserBreakdownParams.reques?= =?UTF-8?q?t=5Ftype=20=E7=B1=BB=E5=9E=8B=E4=BB=8E=20number=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=20UsageRequestType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/admin/dashboard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/api/admin/dashboard.ts b/frontend/src/api/admin/dashboard.ts index 97c16aa362..ae20d33f8e 100644 --- a/frontend/src/api/admin/dashboard.ts +++ b/frontend/src/api/admin/dashboard.ts @@ -173,7 +173,7 @@ export interface UserBreakdownParams { user_id?: number api_key_id?: number account_id?: number - request_type?: number + request_type?: UsageRequestType stream?: boolean billing_type?: number | null }