From 26a8030e8daeb5363353c1e6c47c82fd056fdf14 Mon Sep 17 00:00:00 2001 From: Yvan <8574526@qq,com> Date: Thu, 5 Nov 2026 09:17:37 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=B1=87=E6=80=BB=E5=90=8E=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=A0=A1=E9=AA=8C=E6=B8=B8=E6=88=8F=E6=9D=83=E9=99=90?= =?UTF-8?q?=E4=BB=A5=E4=BC=98=E5=8C=96=E5=88=86=E7=BA=A7=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/data/business/analytics.go | 37 ++++++++++++++++++++++-- internal/data/business/analytics_test.go | 26 +++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/internal/data/business/analytics.go b/internal/data/business/analytics.go index 1e09daa..88a135b 100644 --- a/internal/data/business/analytics.go +++ b/internal/data/business/analytics.go @@ -84,9 +84,6 @@ type analyticsAggPO struct { func analyticsRoot(db *gorm.DB, kind business.AnalyticsKind, scope business.AnalyticsScope, f business.AnalyticsFilter, table string, mode int, pay bool) (*gorm.DB, string) { q := analyticsQuery(db, scope, f, table, mode) - if kind == business.GameAnalytics { - q = q.Where("(r.source_db,r.game_id) IN (?)", approvedGamesQuery(db, scope.Viewer, f.Sources).Select("g.source_db,g.id")) - } id := "r.game_id" if kind == business.ChannelAnalytics { id = "r.promote_id" @@ -216,6 +213,11 @@ func (r *analyticsRepo) Aggregate(ctx context.Context, kind business.AnalyticsKi return nil, err } } + if kind == business.GameAnalytics { + if err := filterAnalyticsGames(db, scope, f, rowMap); err != nil { + return nil, err + } + } if withGlobal { totals := toPortraitCounts(global) result.GlobalCounts, result.GlobalPay = &totals, globalPay @@ -280,6 +282,35 @@ func analyticsPairs(keys []business.AnalyticsKey) [][]any { } return result } + +// Aggregate only visible-channel facts, then check game authorization once per +// resulting game. Checking approved applications inside each report scan can +// force MySQL 5.7 to repeatedly expand the application table before grouping. +func filterAnalyticsGames(db *gorm.DB, scope business.AnalyticsScope, f business.AnalyticsFilter, rows map[business.AnalyticsKey]*business.AnalyticsRow) error { + keys := make([]business.AnalyticsKey, 0, len(rows)) + for key := range rows { + keys = append(keys, key) + } + allowed := map[business.AnalyticsKey]bool{} + for start := 0; start < len(keys); start += 300 { + var games []analyticsAggPO + q := approvedGamesQuery(db, scope.Viewer, f.Sources).Where("(g.source_db,g.id) IN ?", analyticsPairs(keys[start:min(start+300, len(keys))])) + if err := q.Select("g.source_db,g.id AS entity_id").Scan(&games).Error; err != nil { + return business.ErrStorage + } + for _, game := range games { + if game.ID != nil { + allowed[business.AnalyticsKey{SourceDB: game.SourceDB, ID: *game.ID}] = true + } + } + } + for key := range rows { + if !allowed[key] { + delete(rows, key) + } + } + return nil +} func filterAnalyticsRows(db *gorm.DB, scope business.AnalyticsScope, f business.AnalyticsFilter, rows map[business.AnalyticsKey]*business.AnalyticsRow) error { keys := []business.AnalyticsKey{} for key := range rows { diff --git a/internal/data/business/analytics_test.go b/internal/data/business/analytics_test.go index d75c265..6385aaa 100644 --- a/internal/data/business/analytics_test.go +++ b/internal/data/business/analytics_test.go @@ -120,6 +120,32 @@ func TestAnalyticsChannelSearchMatchesNicknameAndKeepsScope(t *testing.T) { t.Fatal("wildcard keyword must not broaden the query") } } + +func TestGameAuthorizationFiltersAggregatedFactsByPlatform(t *testing.T) { + db, repo, scope, filter := prepareAnalyticsData(t) + scope.Viewer = business.UserScope{SelfID: 9, OnlySelf: true} + for _, sql := range []string{ + "INSERT INTO tab_game(source_db,id,game_name) VALUES('lx',77,'未申请游戏'),('hw',77,'其他平台游戏')", + "INSERT INTO tab_promote_apply(source_db,promote_id,game_id,status) VALUES('hw',100,77,1)", + "INSERT INTO tab_sw_report_day(source_db,promote_id,game_id,dedup_mode,stat_date,total_pay,game_name) VALUES('lx',100,77,0,'2026-11-01',9999999,'未申请游戏')", + } { + if err := db.Exec(sql).Error; err != nil { + t.Fatal(err) + } + } + data, err := repo.Aggregate(context.Background(), business.GameAnalytics, scope, filter) + if err != nil { + t.Fatal(err) + } + if len(data.Rows) == 0 { + t.Fatal("approved games must remain visible") + } + for _, row := range data.Rows { + if row.ID == 77 || row.SourceDB != "lx" { + t.Fatal("unapproved game facts or another platform's approval escaped authorization") + } + } +} func TestAnalyticsMissingScopeAndTablesDoNotBroaden(t *testing.T) { db, repo, scope, filter := prepareAnalyticsData(t) scope.IncludeSub = false