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
+18 -3
View File
@@ -213,9 +213,6 @@ func (r *analyticsRepo) Aggregate(ctx context.Context, kind business.AnalyticsKi
return result, nil
}
}
if err := fillAnalyticsNames(db, kind, scope, f, rowMap); err != nil {
return nil, err
}
for _, row := range rowMap {
result.Rows = append(result.Rows, row)
}
@@ -228,6 +225,18 @@ func (r *analyticsRepo) Aggregate(ctx context.Context, kind business.AnalyticsKi
})
return result, nil
}
func (r *analyticsRepo) FillNames(ctx context.Context, kind business.AnalyticsKind, scope business.AnalyticsScope, f business.AnalyticsFilter, rows []*business.AnalyticsRow) error {
if len(rows) == 0 {
return nil
}
byKey := make(map[business.AnalyticsKey]*business.AnalyticsRow, len(rows))
for _, row := range rows {
byKey[row.AnalyticsKey] = row
}
return fillAnalyticsNames(r.data.DB().WithContext(ctx), kind, scope, f, byKey)
}
func analyticsPairs(keys []business.AnalyticsKey) [][]any {
result := make([][]any, 0, len(keys))
for _, key := range keys {
@@ -269,6 +278,12 @@ func fillAnalyticsNames(db *gorm.DB, kind business.AnalyticsKind, scope business
for key := range rows {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool {
if keys[i].ID != keys[j].ID {
return keys[i].ID < keys[j].ID
}
return keys[i].SourceDB < keys[j].SourceDB
})
type entityPO struct {
ID int64
SourceDB string `gorm:"column:source_db"`
+12
View File
@@ -34,6 +34,9 @@ func TestAnalyticsChannelTreeAndAdminOfficialRows(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if err := repo.FillNames(context.Background(), business.ChannelAnalytics, scope, filter, data.Rows); err != nil {
t.Fatal(err)
}
if len(data.Rows) != 3 || data.GlobalCounts != nil {
t.Fatalf("普通商务统计范围错误: %+v", data)
}
@@ -69,6 +72,12 @@ func TestAnalyticsFiltersKeepHierarchyAndSnapshotNames(t *testing.T) {
kind := 1
filter.PromoteType = &kind
data, err = repo.Aggregate(context.Background(), business.ChannelAnalytics, scope, filter)
if err != nil {
t.Fatal(err)
}
if err := repo.FillNames(context.Background(), business.ChannelAnalytics, scope, filter, data.Rows); err != nil {
t.Fatal(err)
}
if err != nil || len(data.Rows) != 1 || data.Rows[0].ID != 100 || portraitTestMoney(data.Rows[0].Pay) != "20.20" || data.Rows[0].PromoteType == nil || *data.Rows[0].PromoteType != 0 {
t.Fatalf("类型筛选应限制参与渠道,同时显示归根后的当前资料: %+v %v", data, err)
}
@@ -78,6 +87,9 @@ func TestAnalyticsFiltersKeepHierarchyAndSnapshotNames(t *testing.T) {
if err != nil || len(data.Rows) != 2 {
t.Fatalf("按游戏快照名筛选错误: %+v %v", data, err)
}
if err := repo.FillNames(context.Background(), business.GameAnalytics, scope, filter, data.Rows); err != nil {
t.Fatal(err)
}
for _, row := range data.Rows {
if row.SourceDB == "lx" && (row.ID != 1 || row.Name != "游戏甲" || row.Counts.Register != 1) {
t.Fatalf("应按报表快照筛选、用当前游戏名展示: %+v", row)
+5 -1
View File
@@ -155,7 +155,11 @@ func (r *serverAnalysisRepo) Servers(ctx context.Context, sources, games []strin
return nil, business.ErrServerOptionsUnavailable
}
}
q := db.Table("tab_game_server s").Where("EXISTS (?)", db.Table("tab_game g").Select("1").Where("g.source_db=s.source_db AND g.id=s.game_id AND g.game_name IN ?", games))
// MySQL 5.7 不会将相关 EXISTS 展开为半连接,导致扫描全部区服。
// 多列 IN 让优化器先按游戏名索引定位,再使用区服的 game_id 索引,
// 同时保留平台匹配和重复游戏资料不扩增结果的语义。
gamesQuery := db.Table("tab_game g").Select("g.source_db,g.id").Where("g.game_name IN ?", games)
q := db.Table("tab_game_server s").Where("(s.source_db,s.game_id) IN (?)", gamesQuery)
if len(sources) > 0 {
q = q.Where("s.source_db IN ?", sources)
}
@@ -85,3 +85,30 @@ func TestServerAnalysisStorageScopeSnapshotAndPreference(t *testing.T) {
t.Fatal("缺身份表应明确未接入")
}
}
func TestServerOptionsPreservePlatformFilterAndDoNotDuplicateMatches(t *testing.T) {
db := openTestDB(t)
for _, sql := range []string{
"CREATE TABLE tab_game(id INTEGER,source_db TEXT,game_name TEXT)",
"INSERT INTO tab_game VALUES(1,'lx','甲'),(1,'lx','甲'),(1,'hw','乙'),(2,'lx','乙')",
"CREATE TABLE tab_game_server(id INTEGER,source_db TEXT,game_id INTEGER,server_num TEXT,server_name TEXT,start_time INTEGER)",
"INSERT INTO tab_game_server VALUES(1,'lx',1,'001','一区',100),(2,'lx',1,'002','二区',200),(3,'hw',1,'001','其他平台',300),(4,'lx',2,'001','其他游戏',400)",
} {
if err := db.Exec(sql).Error; err != nil {
t.Fatal(err)
}
}
repo := NewServerAnalysisRepo(testData{db})
rows, err := repo.Servers(context.Background(), nil, []string{"甲"}, "", 100)
if err != nil || len(rows) != 2 || rows[0].Name != "一区" || rows[1].Number != "002" {
t.Fatalf("相同游戏资料不能扩增区服,异平台同号不能匹配: %+v %v", rows, err)
}
rows, err = repo.Servers(context.Background(), []string{"lx"}, []string{"甲", "乙"}, "区", 1)
if err != nil || len(rows) != 1 || rows[0].Name != "一区" {
t.Fatalf("名称筛选、排序或数量限制改变: %+v %v", rows, err)
}
rows, err = repo.Servers(context.Background(), []string{"hw"}, []string{"甲"}, "", 100)
if err != nil || len(rows) != 0 {
t.Fatalf("平台筛选被忽略: %+v %v", rows, err)
}
}