perf: 减少统计页面资料读取并优化区服选项查询

This commit is contained in:
Yvan
2026-11-04 16:42:11 +08:00
parent 503f3b1a7d
commit 2efce4d821
8 changed files with 184 additions and 13 deletions
+30 -4
View File
@@ -69,6 +69,7 @@ type AnalyticsPage struct {
}
type AnalyticsRepo interface {
Aggregate(context.Context, AnalyticsKind, AnalyticsScope, AnalyticsFilter) (*AnalyticsData, error)
FillNames(context.Context, AnalyticsKind, AnalyticsScope, AnalyticsFilter, []*AnalyticsRow) error
}
type AnalyticsUsecase struct {
repo AnalyticsRepo
@@ -138,7 +139,8 @@ func (uc *AnalyticsUsecase) List(ctx context.Context, kind AnalyticsKind, filter
filter.Begin = filter.StartDate.Format("2006-01-02")
filter.End = filter.EndDate.Format("2006-01-02")
out := &AnalyticsPage{Rows: []*AnalyticsRow{}, Begin: filter.Begin, End: filter.End, Available: true}
data, err := uc.repo.Aggregate(ctx, kind, AnalyticsScope{Viewer: viewer, IncludeSub: pref.IncludeSubPromote, IDCardDedup: pref.IDCardDedup}, filter)
scope := AnalyticsScope{Viewer: viewer, IncludeSub: pref.IncludeSubPromote, IDCardDedup: pref.IDCardDedup}
data, err := uc.repo.Aggregate(ctx, kind, scope, filter)
if err != nil {
return nil, err
}
@@ -165,9 +167,6 @@ func (uc *AnalyticsUsecase) List(ctx context.Context, kind AnalyticsKind, filter
pay.Add(pay, rounded)
}
counts = analyticsAdd(counts, row.Counts)
if kind == ChannelAnalytics {
row.Name = (&LedgerNames{PromoteID: &row.ID, PromoteAccount: row.Name}).DisplayPromote()
}
}
if kind == ChannelAnalytics {
counts = analyticsCap(counts)
@@ -187,6 +186,28 @@ func (uc *AnalyticsUsecase) List(ctx context.Context, kind AnalyticsKind, filter
if err := analyticsValues(out.Summary); err != nil {
return nil, err
}
// 仅按渠道名称或类型排序时需要提前读取全部资料;数字指标排序
// 确定当前页后再补名称和商务信息,汇总仍使用全部可见统计行。
namesBeforeSort := kind == ChannelAnalytics && (filter.SortField == "promoteName" || filter.SortField == "promoteType")
fillNames := func(rows []*AnalyticsRow) error {
if len(rows) == 0 {
return nil
}
if err := uc.repo.FillNames(ctx, kind, scope, filter, rows); err != nil {
return err
}
if kind == ChannelAnalytics {
for _, row := range rows {
row.Name = (&LedgerNames{PromoteID: &row.ID, PromoteAccount: row.Name}).DisplayPromote()
}
}
return nil
}
if namesBeforeSort {
if err := fillNames(data.Rows); err != nil {
return nil, err
}
}
if err := sortAnalytics(data.Rows, kind, filter.SortField, filter.Ascending); err != nil {
return nil, err
}
@@ -205,6 +226,11 @@ func (uc *AnalyticsUsecase) List(ctx context.Context, kind AnalyticsKind, filter
if from < len(data.Rows) {
out.Rows = data.Rows[from:to]
}
if !namesBeforeSort {
if err := fillNames(out.Rows); err != nil {
return nil, err
}
}
return out, nil
}
func sortAnalytics(rows []*AnalyticsRow, kind AnalyticsKind, field string, ascending bool) error {
+89 -3
View File
@@ -7,9 +7,23 @@ import (
)
type analyticsRepoFake struct {
data *AnalyticsData
scope AnalyticsScope
filter AnalyticsFilter
data *AnalyticsData
scope AnalyticsScope
filter AnalyticsFilter
fillCalls [][]AnalyticsKey
fill func([]*AnalyticsRow) error
}
func (r *analyticsRepoFake) FillNames(_ context.Context, _ AnalyticsKind, _ AnalyticsScope, _ AnalyticsFilter, rows []*AnalyticsRow) error {
keys := make([]AnalyticsKey, len(rows))
for i, row := range rows {
keys[i] = row.AnalyticsKey
}
r.fillCalls = append(r.fillCalls, keys)
if r.fill != nil {
return r.fill(rows)
}
return nil
}
func (r *analyticsRepoFake) Aggregate(_ context.Context, _ AnalyticsKind, s AnalyticsScope, f AnalyticsFilter) (*AnalyticsData, error) {
@@ -76,3 +90,75 @@ func TestAnalyticsSortAndMissingData(t *testing.T) {
t.Fatal("倒置日期应被拒绝")
}
}
func TestAnalyticsLoadsMetadataOnlyForSelectedPage(t *testing.T) {
for _, kind := range []AnalyticsKind{ChannelAnalytics, GameAnalytics} {
t.Run(string(kind), func(t *testing.T) {
uc, repo := analyticsFixture()
for _, row := range repo.data.Rows {
row.Name = ""
}
repo.fill = func(rows []*AnalyticsRow) error {
for _, row := range rows {
row.Name = "本页资料"
row.BindingAccount = "本页商务"
}
return nil
}
page, err := uc.List(accountContext(7, 901), kind, AnalyticsFilter{SortField: "totalPay"}, 1, 1)
if err != nil {
t.Fatal(err)
}
if len(repo.fillCalls) != 1 || len(repo.fillCalls[0]) != 1 || repo.fillCalls[0][0].ID != 2 {
t.Fatalf("应只读取排序后当前页的资料: %+v", repo.fillCalls)
}
if page.Total != 2 || page.Summary.Pay != "50.00" || page.Rows[0].Name != "本页资料" || page.Rows[0].BindingAccount != "本页商务" {
t.Fatalf("延迟读取影响汇总或展示资料: %+v", page)
}
repo.fillCalls = nil
page, err = uc.List(accountContext(7, 901), kind, AnalyticsFilter{}, 3, 1)
if err != nil || len(page.Rows) != 0 || len(repo.fillCalls) != 0 || page.Summary.Pay != "50.00" {
t.Fatal("空页不应读取资料,汇总仍须保留")
}
repo.fill = func([]*AnalyticsRow) error { return ErrStorage }
if _, err := uc.List(accountContext(7, 901), kind, AnalyticsFilter{}, 1, 1); err != ErrStorage {
t.Fatalf("资料读取错误必须向上传递: %v", err)
}
})
}
}
func TestAnalyticsLoadsMetadataBeforeNameOrTypeSort(t *testing.T) {
for _, field := range []string{"promoteName", "promoteType"} {
t.Run(field, func(t *testing.T) {
uc, repo := analyticsFixture()
for _, row := range repo.data.Rows {
row.Name = ""
}
repo.fill = func(rows []*AnalyticsRow) error {
for _, row := range rows {
name, kind := "Zulu", 9
if row.ID == 2 {
name, kind = "Alpha", 1
}
row.Name, row.PromoteType = name, &kind
}
return nil
}
page, err := uc.List(accountContext(7, 901), ChannelAnalytics, AnalyticsFilter{SortField: field, Ascending: true}, 1, 1)
if err != nil || len(page.Rows) != 1 || page.Rows[0].ID != 2 || len(repo.fillCalls) != 1 || len(repo.fillCalls[0]) != 2 {
t.Fatalf("按资料字段排序前必须读取全体资料: page=%+v calls=%+v err=%v", page, repo.fillCalls, err)
}
})
}
}
func TestAnalyticsDeferredNamesPreserveOfficialAndUnknownLabels(t *testing.T) {
uc, repo := analyticsFixture()
repo.data.Rows[0].ID, repo.data.Rows[0].Name = 0, ""
repo.data.Rows[1].Name = ""
page, err := uc.List(accountContext(1, 888), ChannelAnalytics, AnalyticsFilter{SortField: "promoteId", Ascending: true}, 1, 10)
if err != nil || page.Rows[0].Name != "官方渠道" || page.Rows[1].Name != "未知渠道(ID:2)" {
t.Fatalf("默认渠道名丢失: %+v %v", page, err)
}
}