fix: 汇总后批量校验游戏权限以优化分级统计
This commit is contained in:
@@ -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) {
|
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)
|
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"
|
id := "r.game_id"
|
||||||
if kind == business.ChannelAnalytics {
|
if kind == business.ChannelAnalytics {
|
||||||
id = "r.promote_id"
|
id = "r.promote_id"
|
||||||
@@ -216,6 +213,11 @@ func (r *analyticsRepo) Aggregate(ctx context.Context, kind business.AnalyticsKi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if kind == business.GameAnalytics {
|
||||||
|
if err := filterAnalyticsGames(db, scope, f, rowMap); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
if withGlobal {
|
if withGlobal {
|
||||||
totals := toPortraitCounts(global)
|
totals := toPortraitCounts(global)
|
||||||
result.GlobalCounts, result.GlobalPay = &totals, globalPay
|
result.GlobalCounts, result.GlobalPay = &totals, globalPay
|
||||||
@@ -280,6 +282,35 @@ func analyticsPairs(keys []business.AnalyticsKey) [][]any {
|
|||||||
}
|
}
|
||||||
return result
|
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 {
|
func filterAnalyticsRows(db *gorm.DB, scope business.AnalyticsScope, f business.AnalyticsFilter, rows map[business.AnalyticsKey]*business.AnalyticsRow) error {
|
||||||
keys := []business.AnalyticsKey{}
|
keys := []business.AnalyticsKey{}
|
||||||
for key := range rows {
|
for key := range rows {
|
||||||
|
|||||||
@@ -120,6 +120,32 @@ func TestAnalyticsChannelSearchMatchesNicknameAndKeepsScope(t *testing.T) {
|
|||||||
t.Fatal("wildcard keyword must not broaden the query")
|
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) {
|
func TestAnalyticsMissingScopeAndTablesDoNotBroaden(t *testing.T) {
|
||||||
db, repo, scope, filter := prepareAnalyticsData(t)
|
db, repo, scope, filter := prepareAnalyticsData(t)
|
||||||
scope.IncludeSub = false
|
scope.IncludeSub = false
|
||||||
|
|||||||
Reference in New Issue
Block a user