Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
5364011a
"frontend/src/vscode:/vscode.git/clone" did not exist on "f1e884ce2b8b9881437c3e451931bc8cf820d662"
Commit
5364011a
authored
Jan 11, 2026
by
yangjianbo
Browse files
fix(仪表盘): 修正聚合时间桶与清理节流
parent
d78f42d2
Changes
3
Show whitespace changes
Inline
Side-by-side
backend/internal/repository/dashboard_aggregation_repo.go
View file @
5364011a
...
...
@@ -43,10 +43,11 @@ func (r *dashboardAggregationRepository) AggregateRange(ctx context.Context, sta
dayEnd
=
dayEnd
.
Add
(
24
*
time
.
Hour
)
}
if
err
:=
r
.
insertHourlyActiveUsers
(
ctx
,
startUTC
,
endUTC
);
err
!=
nil
{
// 以桶边界聚合,允许覆盖 end 所在桶的剩余区间。
if
err
:=
r
.
insertHourlyActiveUsers
(
ctx
,
hourStart
,
hourEnd
);
err
!=
nil
{
return
err
}
if
err
:=
r
.
insertDailyActiveUsers
(
ctx
,
startUTC
,
endUTC
);
err
!=
nil
{
if
err
:=
r
.
insertDailyActiveUsers
(
ctx
,
hourStart
,
hourEnd
);
err
!=
nil
{
return
err
}
if
err
:=
r
.
upsertHourlyAggregates
(
ctx
,
hourStart
,
hourEnd
);
err
!=
nil
{
...
...
@@ -138,10 +139,10 @@ func (r *dashboardAggregationRepository) insertDailyActiveUsers(ctx context.Cont
query
:=
`
INSERT INTO usage_dashboard_daily_users (bucket_date, user_id)
SELECT DISTINCT
(
created_a
t AT TIME ZONE 'UTC')::date AS bucket_date,
(
bucket_star
t AT TIME ZONE 'UTC')::date AS bucket_date,
user_id
FROM usage_
log
s
WHERE
created_a
t >= $1 AND
created_a
t < $2
FROM usage_
dashboard_hourly_user
s
WHERE
bucket_star
t >= $1 AND
bucket_star
t < $2
ON CONFLICT DO NOTHING
`
_
,
err
:=
r
.
sql
.
ExecContext
(
ctx
,
query
,
start
.
UTC
(),
end
.
UTC
())
...
...
backend/internal/service/dashboard_aggregation_service.go
View file @
5364011a
...
...
@@ -134,12 +134,12 @@ func (s *DashboardAggregationService) runScheduledAggregation() {
}
lookback
:=
time
.
Duration
(
s
.
cfg
.
LookbackSeconds
)
*
time
.
Second
start
:=
last
.
Add
(
-
lookback
)
epoch
:=
time
.
Unix
(
0
,
0
)
.
UTC
()
start
:=
last
.
Add
(
-
lookback
)
if
!
last
.
After
(
epoch
)
{
start
=
now
.
Add
(
-
lookback
)
}
if
start
.
After
(
now
)
{
// 首次聚合覆盖当天,避免只统计最后一个窗口。
start
=
truncateToDayUTC
(
now
)
}
else
if
start
.
After
(
now
)
{
start
=
now
.
Add
(
-
lookback
)
}
...
...
@@ -204,17 +204,21 @@ func (s *DashboardAggregationService) maybeCleanupRetention(ctx context.Context,
return
}
}
s
.
lastRetentionCleanup
.
Store
(
now
)
hourlyCutoff
:=
now
.
AddDate
(
0
,
0
,
-
s
.
cfg
.
Retention
.
HourlyDays
)
dailyCutoff
:=
now
.
AddDate
(
0
,
0
,
-
s
.
cfg
.
Retention
.
DailyDays
)
usageCutoff
:=
now
.
AddDate
(
0
,
0
,
-
s
.
cfg
.
Retention
.
UsageLogsDays
)
if
err
:=
s
.
repo
.
CleanupAggregates
(
ctx
,
hourlyCutoff
,
dailyCutoff
);
err
!=
nil
{
log
.
Printf
(
"[DashboardAggregation] 聚合保留清理失败: %v"
,
err
)
aggErr
:=
s
.
repo
.
CleanupAggregates
(
ctx
,
hourlyCutoff
,
dailyCutoff
)
if
aggErr
!=
nil
{
log
.
Printf
(
"[DashboardAggregation] 聚合保留清理失败: %v"
,
aggErr
)
}
usageErr
:=
s
.
repo
.
CleanupUsageLogs
(
ctx
,
usageCutoff
)
if
usageErr
!=
nil
{
log
.
Printf
(
"[DashboardAggregation] usage_logs 保留清理失败: %v"
,
usageErr
)
}
if
e
rr
:
=
s
.
repo
.
CleanupUsageLogs
(
ctx
,
usageCutoff
);
e
rr
!
=
nil
{
log
.
Printf
(
"[DashboardAggregation] usage_logs 保留清理失败: %v"
,
err
)
if
aggE
rr
=
=
nil
&&
usageE
rr
=
=
nil
{
s
.
lastRetentionCleanup
.
Store
(
now
)
}
}
...
...
backend/internal/service/dashboard_aggregation_service_test.go
0 → 100644
View file @
5364011a
package
service
import
(
"context"
"errors"
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/stretchr/testify/require"
)
type
dashboardAggregationRepoTestStub
struct
{
aggregateCalls
int
lastStart
time
.
Time
lastEnd
time
.
Time
watermark
time
.
Time
aggregateErr
error
cleanupAggregatesErr
error
cleanupUsageErr
error
}
func
(
s
*
dashboardAggregationRepoTestStub
)
AggregateRange
(
ctx
context
.
Context
,
start
,
end
time
.
Time
)
error
{
s
.
aggregateCalls
++
s
.
lastStart
=
start
s
.
lastEnd
=
end
return
s
.
aggregateErr
}
func
(
s
*
dashboardAggregationRepoTestStub
)
GetAggregationWatermark
(
ctx
context
.
Context
)
(
time
.
Time
,
error
)
{
return
s
.
watermark
,
nil
}
func
(
s
*
dashboardAggregationRepoTestStub
)
UpdateAggregationWatermark
(
ctx
context
.
Context
,
aggregatedAt
time
.
Time
)
error
{
return
nil
}
func
(
s
*
dashboardAggregationRepoTestStub
)
CleanupAggregates
(
ctx
context
.
Context
,
hourlyCutoff
,
dailyCutoff
time
.
Time
)
error
{
return
s
.
cleanupAggregatesErr
}
func
(
s
*
dashboardAggregationRepoTestStub
)
CleanupUsageLogs
(
ctx
context
.
Context
,
cutoff
time
.
Time
)
error
{
return
s
.
cleanupUsageErr
}
func
(
s
*
dashboardAggregationRepoTestStub
)
EnsureUsageLogsPartitions
(
ctx
context
.
Context
,
now
time
.
Time
)
error
{
return
nil
}
func
TestDashboardAggregationService_RunScheduledAggregation_EpochUsesDayStart
(
t
*
testing
.
T
)
{
repo
:=
&
dashboardAggregationRepoTestStub
{
watermark
:
time
.
Unix
(
0
,
0
)
.
UTC
()}
svc
:=
&
DashboardAggregationService
{
repo
:
repo
,
cfg
:
config
.
DashboardAggregationConfig
{
Enabled
:
true
,
IntervalSeconds
:
60
,
LookbackSeconds
:
120
,
Retention
:
config
.
DashboardAggregationRetentionConfig
{
UsageLogsDays
:
1
,
HourlyDays
:
1
,
DailyDays
:
1
,
},
},
}
svc
.
runScheduledAggregation
()
require
.
Equal
(
t
,
1
,
repo
.
aggregateCalls
)
require
.
False
(
t
,
repo
.
lastEnd
.
IsZero
())
require
.
Equal
(
t
,
truncateToDayUTC
(
repo
.
lastEnd
),
repo
.
lastStart
)
}
func
TestDashboardAggregationService_CleanupRetentionFailure_DoesNotRecord
(
t
*
testing
.
T
)
{
repo
:=
&
dashboardAggregationRepoTestStub
{
cleanupAggregatesErr
:
errors
.
New
(
"清理失败"
)}
svc
:=
&
DashboardAggregationService
{
repo
:
repo
,
cfg
:
config
.
DashboardAggregationConfig
{
Retention
:
config
.
DashboardAggregationRetentionConfig
{
UsageLogsDays
:
1
,
HourlyDays
:
1
,
DailyDays
:
1
,
},
},
}
svc
.
maybeCleanupRetention
(
context
.
Background
(),
time
.
Now
()
.
UTC
())
require
.
Nil
(
t
,
svc
.
lastRetentionCleanup
.
Load
())
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment