Skip to content

Commit d8aff3a

Browse files
committed
Merge origin/main into fix/account-extra-scheduler-pressure-20260311
2 parents 2b30e3b + c0110cb commit d8aff3a

73 files changed

Lines changed: 5716 additions & 428 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/internal/domain/constants.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,12 @@ var DefaultAntigravityModelMapping = map[string]string{
8484
"claude-haiku-4-5": "claude-sonnet-4-5",
8585
"claude-haiku-4-5-20251001": "claude-sonnet-4-5",
8686
// Gemini 2.5 白名单
87-
"gemini-2.5-flash": "gemini-2.5-flash",
88-
"gemini-2.5-flash-lite": "gemini-2.5-flash-lite",
89-
"gemini-2.5-flash-thinking": "gemini-2.5-flash-thinking",
90-
"gemini-2.5-pro": "gemini-2.5-pro",
87+
"gemini-2.5-flash": "gemini-2.5-flash",
88+
"gemini-2.5-flash-image": "gemini-2.5-flash-image",
89+
"gemini-2.5-flash-image-preview": "gemini-2.5-flash-image",
90+
"gemini-2.5-flash-lite": "gemini-2.5-flash-lite",
91+
"gemini-2.5-flash-thinking": "gemini-2.5-flash-thinking",
92+
"gemini-2.5-pro": "gemini-2.5-pro",
9193
// Gemini 3 白名单
9294
"gemini-3-flash": "gemini-3-flash",
9395
"gemini-3-pro-high": "gemini-3-pro-high",

backend/internal/domain/constants_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ func TestDefaultAntigravityModelMapping_ImageCompatibilityAliases(t *testing.T)
66
t.Parallel()
77

88
cases := map[string]string{
9+
"gemini-2.5-flash-image": "gemini-2.5-flash-image",
10+
"gemini-2.5-flash-image-preview": "gemini-2.5-flash-image",
911
"gemini-3.1-flash-image": "gemini-3.1-flash-image",
1012
"gemini-3.1-flash-image-preview": "gemini-3.1-flash-image",
1113
"gemini-3-pro-image": "gemini-3.1-flash-image",

backend/internal/handler/admin/account_handler.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ func (h *AccountHandler) Delete(c *gin.Context) {
628628
// TestAccountRequest represents the request body for testing an account
629629
type TestAccountRequest struct {
630630
ModelID string `json:"model_id"`
631+
Prompt string `json:"prompt"`
631632
}
632633

633634
type SyncFromCRSRequest struct {
@@ -658,7 +659,7 @@ func (h *AccountHandler) Test(c *gin.Context) {
658659
_ = c.ShouldBindJSON(&req)
659660

660661
// Use AccountTestService to test the account with SSE streaming
661-
if err := h.accountTestService.TestAccountConnection(c, accountID, req.ModelID); err != nil {
662+
if err := h.accountTestService.TestAccountConnection(c, accountID, req.ModelID, req.Prompt); err != nil {
662663
// Error already sent via SSE, just log
663664
return
664665
}

backend/internal/handler/admin/dashboard_handler.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,12 @@ func (h *DashboardHandler) GetUsageTrend(c *gin.Context) {
249249
}
250250
}
251251

252-
trend, err := h.dashboardService.GetUsageTrendWithFilters(c.Request.Context(), startTime, endTime, granularity, userID, apiKeyID, accountID, groupID, model, requestType, stream, billingType)
252+
trend, hit, err := h.getUsageTrendCached(c.Request.Context(), startTime, endTime, granularity, userID, apiKeyID, accountID, groupID, model, requestType, stream, billingType)
253253
if err != nil {
254254
response.Error(c, 500, "Failed to get usage trend")
255255
return
256256
}
257+
c.Header("X-Snapshot-Cache", cacheStatusValue(hit))
257258

258259
response.Success(c, gin.H{
259260
"trend": trend,
@@ -321,11 +322,12 @@ func (h *DashboardHandler) GetModelStats(c *gin.Context) {
321322
}
322323
}
323324

324-
stats, err := h.dashboardService.GetModelStatsWithFilters(c.Request.Context(), startTime, endTime, userID, apiKeyID, accountID, groupID, requestType, stream, billingType)
325+
stats, hit, err := h.getModelStatsCached(c.Request.Context(), startTime, endTime, userID, apiKeyID, accountID, groupID, requestType, stream, billingType)
325326
if err != nil {
326327
response.Error(c, 500, "Failed to get model statistics")
327328
return
328329
}
330+
c.Header("X-Snapshot-Cache", cacheStatusValue(hit))
329331

330332
response.Success(c, gin.H{
331333
"models": stats,
@@ -391,11 +393,12 @@ func (h *DashboardHandler) GetGroupStats(c *gin.Context) {
391393
}
392394
}
393395

394-
stats, err := h.dashboardService.GetGroupStatsWithFilters(c.Request.Context(), startTime, endTime, userID, apiKeyID, accountID, groupID, requestType, stream, billingType)
396+
stats, hit, err := h.getGroupStatsCached(c.Request.Context(), startTime, endTime, userID, apiKeyID, accountID, groupID, requestType, stream, billingType)
395397
if err != nil {
396398
response.Error(c, 500, "Failed to get group statistics")
397399
return
398400
}
401+
c.Header("X-Snapshot-Cache", cacheStatusValue(hit))
399402

400403
response.Success(c, gin.H{
401404
"groups": stats,
@@ -416,11 +419,12 @@ func (h *DashboardHandler) GetAPIKeyUsageTrend(c *gin.Context) {
416419
limit = 5
417420
}
418421

419-
trend, err := h.dashboardService.GetAPIKeyUsageTrend(c.Request.Context(), startTime, endTime, granularity, limit)
422+
trend, hit, err := h.getAPIKeyUsageTrendCached(c.Request.Context(), startTime, endTime, granularity, limit)
420423
if err != nil {
421424
response.Error(c, 500, "Failed to get API key usage trend")
422425
return
423426
}
427+
c.Header("X-Snapshot-Cache", cacheStatusValue(hit))
424428

425429
response.Success(c, gin.H{
426430
"trend": trend,
@@ -442,11 +446,12 @@ func (h *DashboardHandler) GetUserUsageTrend(c *gin.Context) {
442446
limit = 12
443447
}
444448

445-
trend, err := h.dashboardService.GetUserUsageTrend(c.Request.Context(), startTime, endTime, granularity, limit)
449+
trend, hit, err := h.getUserUsageTrendCached(c.Request.Context(), startTime, endTime, granularity, limit)
446450
if err != nil {
447451
response.Error(c, 500, "Failed to get user usage trend")
448452
return
449453
}
454+
c.Header("X-Snapshot-Cache", cacheStatusValue(hit))
450455

451456
response.Success(c, gin.H{
452457
"trend": trend,
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package admin
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"net/http/httptest"
7+
"sync/atomic"
8+
"testing"
9+
"time"
10+
11+
"github.com/Wei-Shaw/sub2api/internal/pkg/usagestats"
12+
"github.com/Wei-Shaw/sub2api/internal/service"
13+
"github.com/gin-gonic/gin"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
type dashboardUsageRepoCacheProbe struct {
18+
service.UsageLogRepository
19+
trendCalls atomic.Int32
20+
usersTrendCalls atomic.Int32
21+
}
22+
23+
func (r *dashboardUsageRepoCacheProbe) GetUsageTrendWithFilters(
24+
ctx context.Context,
25+
startTime, endTime time.Time,
26+
granularity string,
27+
userID, apiKeyID, accountID, groupID int64,
28+
model string,
29+
requestType *int16,
30+
stream *bool,
31+
billingType *int8,
32+
) ([]usagestats.TrendDataPoint, error) {
33+
r.trendCalls.Add(1)
34+
return []usagestats.TrendDataPoint{{
35+
Date: "2026-03-11",
36+
Requests: 1,
37+
TotalTokens: 2,
38+
Cost: 3,
39+
ActualCost: 4,
40+
}}, nil
41+
}
42+
43+
func (r *dashboardUsageRepoCacheProbe) GetUserUsageTrend(
44+
ctx context.Context,
45+
startTime, endTime time.Time,
46+
granularity string,
47+
limit int,
48+
) ([]usagestats.UserUsageTrendPoint, error) {
49+
r.usersTrendCalls.Add(1)
50+
return []usagestats.UserUsageTrendPoint{{
51+
Date: "2026-03-11",
52+
UserID: 1,
53+
Email: "cache@test.dev",
54+
Requests: 2,
55+
Tokens: 20,
56+
Cost: 2,
57+
ActualCost: 1,
58+
}}, nil
59+
}
60+
61+
func resetDashboardReadCachesForTest() {
62+
dashboardTrendCache = newSnapshotCache(30 * time.Second)
63+
dashboardUsersTrendCache = newSnapshotCache(30 * time.Second)
64+
dashboardAPIKeysTrendCache = newSnapshotCache(30 * time.Second)
65+
dashboardModelStatsCache = newSnapshotCache(30 * time.Second)
66+
dashboardGroupStatsCache = newSnapshotCache(30 * time.Second)
67+
dashboardSnapshotV2Cache = newSnapshotCache(30 * time.Second)
68+
}
69+
70+
func TestDashboardHandler_GetUsageTrend_UsesCache(t *testing.T) {
71+
t.Cleanup(resetDashboardReadCachesForTest)
72+
resetDashboardReadCachesForTest()
73+
74+
gin.SetMode(gin.TestMode)
75+
repo := &dashboardUsageRepoCacheProbe{}
76+
dashboardSvc := service.NewDashboardService(repo, nil, nil, nil)
77+
handler := NewDashboardHandler(dashboardSvc, nil)
78+
router := gin.New()
79+
router.GET("/admin/dashboard/trend", handler.GetUsageTrend)
80+
81+
req1 := httptest.NewRequest(http.MethodGet, "/admin/dashboard/trend?start_date=2026-03-01&end_date=2026-03-07&granularity=day", nil)
82+
rec1 := httptest.NewRecorder()
83+
router.ServeHTTP(rec1, req1)
84+
require.Equal(t, http.StatusOK, rec1.Code)
85+
require.Equal(t, "miss", rec1.Header().Get("X-Snapshot-Cache"))
86+
87+
req2 := httptest.NewRequest(http.MethodGet, "/admin/dashboard/trend?start_date=2026-03-01&end_date=2026-03-07&granularity=day", nil)
88+
rec2 := httptest.NewRecorder()
89+
router.ServeHTTP(rec2, req2)
90+
require.Equal(t, http.StatusOK, rec2.Code)
91+
require.Equal(t, "hit", rec2.Header().Get("X-Snapshot-Cache"))
92+
require.Equal(t, int32(1), repo.trendCalls.Load())
93+
}
94+
95+
func TestDashboardHandler_GetUserUsageTrend_UsesCache(t *testing.T) {
96+
t.Cleanup(resetDashboardReadCachesForTest)
97+
resetDashboardReadCachesForTest()
98+
99+
gin.SetMode(gin.TestMode)
100+
repo := &dashboardUsageRepoCacheProbe{}
101+
dashboardSvc := service.NewDashboardService(repo, nil, nil, nil)
102+
handler := NewDashboardHandler(dashboardSvc, nil)
103+
router := gin.New()
104+
router.GET("/admin/dashboard/users-trend", handler.GetUserUsageTrend)
105+
106+
req1 := httptest.NewRequest(http.MethodGet, "/admin/dashboard/users-trend?start_date=2026-03-01&end_date=2026-03-07&granularity=day&limit=8", nil)
107+
rec1 := httptest.NewRecorder()
108+
router.ServeHTTP(rec1, req1)
109+
require.Equal(t, http.StatusOK, rec1.Code)
110+
require.Equal(t, "miss", rec1.Header().Get("X-Snapshot-Cache"))
111+
112+
req2 := httptest.NewRequest(http.MethodGet, "/admin/dashboard/users-trend?start_date=2026-03-01&end_date=2026-03-07&granularity=day&limit=8", nil)
113+
rec2 := httptest.NewRecorder()
114+
router.ServeHTTP(rec2, req2)
115+
require.Equal(t, http.StatusOK, rec2.Code)
116+
require.Equal(t, "hit", rec2.Header().Get("X-Snapshot-Cache"))
117+
require.Equal(t, int32(1), repo.usersTrendCalls.Load())
118+
}

0 commit comments

Comments
 (0)